Rendering Apex generic Report Form (part 3 of 5)

Demo: https://apex.oracle.com/pls/apex/f?p=80981:3

The old stuff

First step is to refresh your memory and have a glance at the (good) old Forms interface. I even have a screen print of a client/server environment (6i) which still works under a VM / Windows XP:

In Oracle Forms the generic Headstart Report Launch Form looks like this:

The parameters can be defined by the Headstart Foundation Application:

The new way in Apex

In this section I will explain how to create the Report Launch Form in Apex. It won’t be a guided instruction for Novice users, although I do my best to explain the steps with enough details.

Step 1: create a blank Page

We need two Regions on the page:
(a) a master Region where the user can select which report he wants to run (action 1.1 of sequence diagram)
and after selecting it
(b) show the list of parameters and possible values in the detailed section (action 2.1 and 2.2 of sequence diagram)

Step 2: create Region for selecting the report

So now we create first a Static Content Region with the name “Select Report” which will contain the Select List for the available reports (mind you, in Forms the reportmodule to run was tight to the menu option so you had as many menu items as possible reports):

Create Region
Identification:
Title: Select Report
Appearance:
Template: Hero
Icon: fa-list

Add some css to the page to suppress the Region title next to the Hero Icon

Page, Css, Inline:

h1.t-HeroRegion-title {
display:none;
}

Create page item P3_REPORT_MODULE in region “Select Report”

Create Page Item
Identification:
Name: P3_REPORT_MODULE
Type: Select List
Label
Label: <empty>
Settings
Page Action on Selection: Submit Page
Appearance:
Template: Optional
Template Options: Use Template Defaults, Stretch Form Item, Large
List of Values
Type: SQL Query
select upper(implementation_name) || ' ' || title || '(' || purpose || ')' d
, id r
from qms_modules
where short_name like 'HSD%'
and short_name not like '%530R'
order by 1
Null Display Value: -- Select Report --

We can now try to run the page and we should get some results

Step 3: add Region for display report parameters

The content of this region is generated by a stored procedure in the database: P003_RENDER_PARAMS. This is the most interesting part of rendering this form and for the sake of readability, the details of it will be explained in step 4. Be aware that for completing step 3 the package APEXDEMO must already be present in the database. If not, just replace the procedure call by a null; statement and come back to it later on.

Create Region
Identification
Title: Parameters
Type: PL/SQL Dynamic Content

begin
apexdemo.p003_render_params -- report selection
( p_mde_id => :P3_REPORT_MODULE ) ;
end;

Appearance
Template: Blank with Attributes
Server-side Condition
Type: Item is NOT NULL
Item: P3_REPORT_MODULE

Add some CSS styling (btw the id #p3_params comes from the p003_render_params procedure):

Page, Css, Inline:

#p3_params select, #p3_params input {
width: 100%;
}
#p3_params span.prompt.required::after {
content: "*";
padding-left: 5px;
color: red;
font-size: 1.7em;
}

Step 4: Rendering procedure P003_RENDER_PARAMS

The source of the content is in principle a key:value pair of the dynamic SQL-statement of the LOV Query or just a free text-field. Hey: do I read LOV Query? In that case we could use APEX_ITEM.SELECT_LIST_FROM_QUERY function for it? Unfortunately, we cannot. As you can see in table 18-16 of Apex 18.2 API documentation the definition of the query is SELECT <description>, <id> FROM <table>, whereas the LOV query of Headstart is defined by SELECT <id>, <description> FROM <table>. Swapping around is not a good idea, because the statement can be composed of complex definitions.
Since we don’t want to touch the QMS-table content, the workaround for it is to process the dynamic SQL-statement into a collection. That is also my main reason for putting this functionality in the database, I don’t like to put a lot of logic in the Apex front-end.

Also here for reason of comprehensibility I will list only parts of the procedure. The readability of the code is hampered by the html-tags which are necessary for a proper layout of the Region. By the way, the html code is just a copy of an ordinary standard report layout, so starting with headings, table definition and footers. I will concentrate on the row-content which is the core of the procedure

PSEUDO code:
procedure
define array to hold dynamic LOV result
begin
for r in (select parameter records from QMS_MDE_PARAMS)
loop
if LOV defined
exec LOV query bulk collect into array
print apex_item.select_list
else
print apex_item.text
end if;
end loop;
end;

Above pseudo code now in somewhat more detail. In the p_attributes parameter of the APEX_ITEM, I use additional data-elements which will be used in Javascript code to generate the parameter string (in JSON format) (action 3.1 of the sequence diagram) which will be sent to the database to validate and process for the Report Server request (action 3.1.1 of sequence diagram).

procedure p003_render_params -- report selection
( p_mde_id  in  number -- report ID
) is

  .. declare other local variables

  type r_lov is record
  ( code        varchar2(255)
  , description varchar2(255)
  ) ;
  type t_lov is table of r_lov;
  l_lov t_lov;
  
begin

  p003_render_region_start; -- keep layout aside as much as possible
  
  for r in ( select mpm.mde_id
             ,      mpm.mpm_subtype
             ,      decode(mpm.mpm_subtype
                    , 'SYS', 'u-hot' -- system parameters
                    , 'u-normal'     -- user parameters
                    ) class
             ,      mpm.name
             ,      mpm.data_type
             ,      mpm.ind_mandatory
             ,      decode(mpm.ind_mandatory
                    , 'Y', 'required'
                    , 'optional'
                    ) class_mandatory
             ,      mpm.ind_uppercase
             ,      mpm.prompt
             ,      mpm.plength
             ,      mpm.default_value
             ,      mpm.default_description
             ,      mpm.hint_text
             ,      mpm.lov_query
             ,      mpm.lov_multi_select
             from   qms_mde_params           mpm
             where  mpm.mde_id             = p_mde_id
               and  mpm.name         not in ('SERVER','ORACLE_SHUTDOWN')
             order by
                    mpm.mpm_subtype desc
             ,      mpm.display_seqno
           )
  loop
  
    if r.lov_query is not null
    then
      begin
        execute immediate r.lov_query
        bulk collect
        into    l_lov
        ;
      exception when others then
        -- log error SQLERRM
        raise_application_error(-20001,'Error in LOV query');
      end;
      
      -- p_list_values
      -- Example: 'Yes;Y,No;N'
      for j in 1 .. l_lov.count
      loop
        -- avoid undesired tokens
        l_lov(j).description := replace(l_lov(j).description, ';');
        l_lov(j).description := replace(l_lov(j).description, ',', ' ');
        if j = 1
        then
          l_list_values := l_lov(j).description || ';' || l_lov(j).code ;
        else
          l_list_values := substr( l_list_values || ',' ||
                                   l_lov(j).description || ';' || l_lov(j).code
                                 , 1, 4000) ;
        end if;
      end loop;
    end if;
    
    if r.lov_query is not null and
       r.lov_multi_select = 'N'
    then -- select list
      sys.htp.prn(apex_item.select_list( p_idx         => 1
                                       , p_list_values => l_list_values
                                       , p_attributes  => 'data-name="'      || r.name          || '"; '||
                                                          'data-uppercase="' || r.ind_uppercase || '"; '||
                                                          'data-required="'  || r.ind_mandatory || '"'
                                       , p_value       => l_default_value
                                       , p_show_null   => 'YES'
                                       , p_null_text   => '-- Select '|| r.prompt ||' --'
                                       )
                 );
    elsif r.lov_query is not null and
          r.lov_multi_select = 'Y'
    then -- multiple select list, no shuttle item available in API 
      sys.htp.prn(apex_item.select_list( p_idx         => 1
                                       , p_list_values => l_list_values
                                       , p_value       => null -- l_default_value, doesn't work, have to figure out yet
                                       , p_attributes  => 'multiple=true; size=7; '||
                                                          'data-name="'      || r.name          || '"; '||
                                                          'data-uppercase="' || r.ind_uppercase || '"; '||
                                                          'data-required="'  || r.ind_mandatory || '"'
                                       , p_show_null   => 'YES'
                                       , p_null_text   => '-- Select '|| r.prompt ||' --'
                                       )
                 );
    else -- ordinary text item
      sys.htp.prn(apex_item.text( p_idx         => 1
                                , p_item_id     => r.name
                                , p_item_label  => r.name
                                , p_attributes  => 'data-name="'      || r.name          || '" '||
                                                   'data-uppercase="' || r.ind_uppercase || '" '||
                                                   'data-required="'  || r.ind_mandatory || '"'
                                , p_value       => l_default_value
                                )
                 );
    end if;
      
  end loop;
  
  -- close table and region
  p003_render_region_end;
  
end p003_render_params;

In Headstart, a multi select would result in a Shuttle Control item, however this item type is not available in Apex. Workaround is to script the Shuttle item in html itself, but that’s up to you. For now, it results in a multi-selectlist.

<< Prev: Apex interacts with Report Server >> Next: Submit Apex report parameters