Sep 03

I have to say that I love APEX 4.0, plugins and dynamic actions make life a lot more simpler as we’re able to avoid getting bogged down in javascript or installing/configuring/modifying framework type code. That is unless you have decided to use Ext JS with APEX…. which is the position I find myself in.

We transform our APEX forms into Ext equivalent ones, either on the fly after the page has loaded or pre-built in the page header and slotted into viewport code. Now for the transformed elements on the page I was hoping we could still use APEX’s builtin dynamic actions (not dynamic action plugins) and mixing in jQuery. In theory I thought this may work given that we reuse the same DOM ID for our transformed Ext input items. I was sadly mistaken. :(

Did I want to miss out on what the inbuilt dynamic actions provides me from a development productivity point of view? No way!! So what to do……. Well there were two options that came to mind (taking into consideration that I don’t want to write a dynamic action plugin for a dynamic action that is available in the APEX IDE. I have the long term vision of transforming existing APEX pages into Ext equivalent ones with minimal changes)

  1. I could override the existing APEX dynamic actions javascript with an Ext equivalent version (this would take some time)
  2. Or I could simply generate my dynamic action code on the fly by using the data dictionary.

I chose the second option for now as it was the easier of the two, as I’m really time constrained at the moment, however the first option is the more strategic path which I intend to go down in the future. That aside, in the space of about 90 minutes I came up with the following solution for hide and show support on our Ext select lists/combos. I’ve only started with this one as it was my immediate requirement and it’s not exactly elegant, but it does work.

Here’s the working prototype code which you put into a PLSQL process that runs before “Page Footer”…

CREATE OR REPLACE PROCEDURE ext_hide_show_dynamic_action
AS

  l_app_id         VARCHAR2(100) := v('APP_ID');
  l_page_id        VARCHAR2(100) := v('APP_PAGE_ID');
  l_function       VARCHAR2(10);
  l_opacity        VARCHAR2(10);
  l_duration       VARCHAR2(10);

  --
  -- Internal procedure to print out our hide/show function, rather than copy and pasting
  -- this into two almost identical calls in the main section
  --
  FUNCTION print_js_function
  ( p_in_affected_elements IN VARCHAR2
  , p_in_function          IN VARCHAR2
  , p_in_duration          IN VARCHAR2
  , p_in_opacity           IN VARCHAR2
  ) RETURN VARCHAR2
  AS

  BEGIN
    --
    -- We need to check for any icons e.g. date picker, spinner etc.
    -- We need to also hide the label and any resizeable text elements
    --
    RETURN 'Ext.select("label[for='||p_in_affected_elements||'],fieldset[id*='||p_in_affected_elements||'],'||
                          '[id='||p_in_affected_elements||'],[id='||p_in_affected_elements||'] + img[class*=x-form-trigger]").'||p_in_function||
              '({'||
                'duration: '||p_in_duration||','||
                'endOpacity: '||p_in_opacity||','||
                'remove: false,'||
                'useDisplay: true'||
              '});';

  END print_js_function;    

BEGIN
  sys.htp.p(wwv_flow_utilities.open_javascript);
  sys.htp.p('Ext.onReady(function() {');
  --
  -- Lets loop through each of our dynamic actions (we are looking for hide operations)
  --
  FOR c IN
  ( SELECT da.when_element                 when_element
    ,      CASE da.when_condition
             WHEN 'equal to' THEN '=='
             WHEN 'is null'  THEN
               --
               -- We need to check for LOV null value settings
               --
               CASE pi.display_as
                 WHEN 'Select List' THEN '== "'||pi.lov_null_value||'"'
                 ELSE '== "undefined"'
               END
             ELSE NULL
           END                             when_condition
    ,      da.when_expression              when_expression
    ,      lower(act.action_name)          action_name
    ,      act.affected_elements           affected_elements
    ,      act.affected_elements_type      affected_elements_type
           --
           -- We have to work out the hide/show action to perform as we may
           -- want to show an item based on a value, or do the opposite and hide
           -- the item. The event result dictates what to do,
           -- i.e. the one with "True" set
           --
    ,      CASE act.dynamic_action_event_result
             WHEN 'False' THEN
               CASE action_code
                 WHEN 'NATIVE_HIDE' THEN 'NATIVE_SHOW'
                 ELSE 'NATIVE_HIDE'
               END
             ELSE act.action_code
           END                             action_code
           --
           -- Ext has a different event for a combo, select is for list item
           -- selection, whereas change is the field changes onblur
           --
    ,      CASE da.when_event_internal_name
             WHEN 'change' THEN
               CASE pi.display_as
                 WHEN 'Select List' THEN 'select'
                 ELSE 'change'
               END
             ElSE lower(act.action_name)
           END                          when_event_internal_name
    ,      rownum                       rn
    FROM   apex_application_page_da      da
    ,      apex_application_page_da_acts act
    ,      apex_application_page_items   pi
    WHERE  da.dynamic_action_id     = act.dynamic_action_id
    AND    da.application_id        = l_app_id
    AND    da.page_id               = l_page_id
    AND    pi.item_name             = da.when_element
    AND    pi.application_id        = da.application_id
    AND    pi.page_id               = da.page_id
    AND    act.execute_on_page_init = 'Yes'
    AND    lower(act.action_name)   = 'hide'
    /*
    --
    -- Lets make sure that the dynamic action condition and authorisations are met
    --
    AND    extjs_utils.auth_condition_check
           ( da.condition_type
           , da.condition_expression1
           , da.condition_expression2
           , da.authorization_scheme_id
           ) = 0

    */
  ) LOOP

    --
    -- We are going to build a standalone function so we can also call it on page load
    --
    sys.htp.p('Ext.app.dynAct'||c.when_element||c.rn|| ' = function() {'||
                 'if (Ext.getCmp("'||c.when_element||'").getValue()'||c.when_condition||c.when_expression||') {'||
                     --
                     -- Our dynamic action tells us whetehr to show or hide
                     -- if our condition is met
                     --
                     print_js_function
                     ( p_in_affected_elements => c.affected_elements
                     , p_in_function          => CASE c.action_code WHEN 'NATIVE_HIDE' THEN 'fadeOut' ELSE 'fadeIn' END
                     , p_in_duration          => '.75'
                     , p_in_opacity           => CASE c.action_code WHEN 'NATIVE_HIDE' THEN '0' ELSE '1' END
                     ) ||
                  '} else {'||
                     --
                     -- We do the opposite action of the above Hide or Show
                     --
                     print_js_function
                     ( p_in_affected_elements => c.affected_elements
                     , p_in_function          => CASE c.action_code WHEN 'NATIVE_HIDE' THEN 'fadeIn' ELSE 'fadeOut' END
                     , p_in_duration          => '.75'
                     , p_in_opacity           => CASE c.action_code WHEN 'NATIVE_HIDE' THEN '1' ELSE '0' END
                     ) ||

                  '}'||
              '}'
    );
    --
    -- Lets add our event handler
    --
    sys.htp.p('Ext.getCmp("'||c.when_element||'").on("'||c.when_event_internal_name||'", Ext.app.dynAct'||c.when_element||c.rn||');');
    --
    -- Lets run our function on Page Load, to worrk out what the display should be
    --
    sys.htp.p('Ext.app.dynAct'||c.when_element||c.rn||'();');

  END LOOP;
  sys.htp.p('});');
  sys.htp.p(wwv_flow_utilities.close_javascript);
--
-- Exception Handling Routine
--
EXCEPTION
  WHEN OTHERS
  THEN
    -- Put your excpetion handling code here
    RAISE;
END ext_hide_show_dynamic_action;
/

Backing out the Ext JS functionality is easy, we simply change our region template (which is responsible for the Ext JS page item transforms) back to a standard apex template and disbale the above process and we still have the exact same functionality of a hide show item, just using the standard APEX features.

If you’re not happy with adding custom PLSQL to either page zero or your pages then maybe creating a new dynamic action plugin is for you. It’s great that we have several choices, it’s just up to you “the developer” to decide which method suits your style/direction.

The one thing I’ve failed to mention about building JSON meta data objects and printing out dynamic javascript, is that we use additional resources by querying the data dictionary all the time (over and above building traditional APEX apps). This of course has performance impact when you start to scale your application(s). The simple solution (if you’re headed down this path) is to cache the outputted objects into custom tables and store these either, session, user, or application based depending upon your application requirement with expiration/invalidation policies. It will be then as simple as doing a check for a cached item and reusing it if it exists rather than querying the data dictionary for every page load.

Finally if you’re looking at developing your first region plugin, then download the following plugin written by Doug Gualt from Sumneva and install it. As there’s not much documentation or anything really useful that out there that walks you through how to do it, so it’s easiest just to edit Doug’s plugin to see how it hangs together. I coded my first region plugin last night (sad I know) and used this as my way to learn. I had my first region plugin (based on the Ext.manual documentation) completed in under 2 hours, it saved me hours! Thanks Doug!

P.S. this is a great way to generate a logo in ascii text, which sits nicely in your plugin header code.

Aug 18

Currently most of the apps we build are built with an Ext JS multi-pane tabbed layout. We normally open up content in the main center pane in a new tab within an iframe.

Now this is normally fine if we’re not opening the same APEX page, but sometimes we do. e.g. we have a single APEX page which lists all our customer information and we simply change an APEX ITEM with a new customer ID to change the page to a new customer. Now this works fine until we open up several customers at the same time into multiple iframes/tabs. Once we’ve done this our page items in session state are updated with the details of the last customer which we’ve opened. If we tab switch back to the first customer and perform, say a grid refresh, we now see the contents of the grid change to the last customer we opened. This is simply because we perform a PPR operation on the report and session state is used for the binds in our report which happen to hold the values for our last customer.

In order to deal with the issue I came up with the following approach to add a “beforeload” event on our Ext Stores or an interceptor function to be called before an APEX PPR call. We don’t always use Ext grids for report data, sometimes we still use APEX reports, usually with the one column unordered list template wrapped in an Ext Panel. We then use an Ext button in the Panel toolbar to control the refresh of the report reusing APEX’s $a_report/PPR function to perform the AJAX refresh.

For the APEX PPR side of things I’ve written an override (based on something similar Stephen Blair has done with jQuery to fade in and out the report contents on refresh) of the $a_report function to call an interceptor function which will be responsible for updating session state just before the actual AJAX report refresh call. There’s one slight issue with the override which I’m hoping someone will resolve for me is that for every refresh an extra DIV wraps around the report contents, this is not visible to the end user, but it is if you look at the DOM. Unfortunately I just don’t have the time to perfect it, and it works just fine for me but things like this really bug me!

// Ext $a_report override
$a_report = function (pId, pMin, pMax, pFetched, pSort) {
    var url = 'f?p=' + Ext.get('pFlowId').getValue() + ':' + Ext.get('pFlowStepId').getValue() + ':' + Ext.get('pInstance').getValue() + ':FLOW_PPR_OUTPUT_R' + pId + '_';
    if ( !! pSort) {
        url += pSort + '::RP::' + Math.random() + '&fsp_region_id=' + pId;
    } else {
        url += 'pg_R_' + pId + ':NO:::' + Math.random() + '&pg_max_rows=' + pMax + '&pg_min_row=' + pMin + '&pg_rows_fetched=' + pFetched;
    }
    Ext.get('report_' + pId + '_catch').fadeOut({
        duration: .75,
        endOpacity: .3
    });
   // Page could have an interceptor call defined by the Developer to execute before the refresh
   if (typeof(Ext.app.$a_report_interceptor) !== "undefined") {
      Ext.app.$a_report_interceptor.call();
   }
   Ext.Ajax.request({
      url: url,
      disableCaching: false,
      method: "GET",
      success: function (response, opts) {
         var div = Ext.get('report_' + pId + '_catch');
         var parent = div.parent();
         div.remove();
         Ext.get(Ext.DomHelper.append(parent.fadeIn({
            duration: 2,
            endOpacity: 1
         }), {
            html: response.responseText
         }));
      },
      failure: function (response, opts) {
         console.log('server-side failure with status code ' + response.status);
      }
   });
}

Then in our page footer we simply add our custom interceptor function, we get the benefit of APEX doing the substitution at page generation time, so we don’t need to perform any javascript function to read the value from a hidden form item on the page. This could also be handled by a PLSQL process, it’s up to you to decide which place works best.

Ext.app.$a_report_interceptor = function () {
   Ext.app.apexHttpPost('Do Nothing', ['P13_CUST_ID'], ['&P13_CUST_ID.']);
}

See the following earlier post for the Ext.app.apexHttpPost function code.

For our Ext grids, here’s a snippet of how we add a “beforeload” event on our store to call a function defined in our report meta data JSON object defined in our page header

var pReportMetaData = {
   ...........
   "beforeStoreLoadFn": function (store, options) {
      Ext.app.apexHttpPost('Do Nothing', ['P13_CUST_ID'], ['&P13_CUST_ID.']);
   }
}
// Lets define a before load function on our store if one is defined in our meta object
// usually used to update session state
if (pReportMetaData.beforeStoreLoadFn) {
   var fn = pReportMetaData.beforeStoreLoadFn;
   pStore.on("beforeload", function (store, options) {
      fn.call(store, options);
   });
}

Hopefully this gives you one idea of how you can potentially workaround session state issues when using the same page, multiple iframes, and AJAX.

Aug 17

In the last blog post I talked about the idea of using region plugins for defining query sources rather than acting as a standalone plugin, and the post before that: transforming apex button references into different Ext form items. This post is focused on bringing those two approaches together. i.e. in our development environment we used a stripped down region plugin to define a query source which is used to supply the list of radio items in an Ext Button Menu within the parent region/report.

Perhaps the following two images will explain what I’m failing to do with words…

In the above two images; the first shows the display of two separate menu’s with different items (it’s been photoshopped to show you two tabs of the tabpanel as the menu’s are on separate tabs). The second shows the APEX IDE and the menu plugins which are used to define the query source for the menu items.

The difference in approach to the “A button is not always a button” post is that instead of building a json object in the page header with our shortcuts and using a function to clean some malformed JSON outputted by our templates. We changed the design to generate the entire region toolbar JSON object in the page header, as it allowed a more cleaner and maintainable approach and opened up the ability to query the APEX data dictionary and process sub regions as we looped through each parent region at a time.

The following code example from our initial prototype (which only works for one menu button per region) should give you an idea of how we query the APEX data dictionary to build the toolbar object


--
-- Lets loop through all our page regions to create our region toolbar for each
--
FOR c IN
( SELECT *
  FROM   apex_application_page_regions
  WHERE  application_id = v_app_id
  AND    page_id        = v_page_id
  AND    source_type    NOT IN ('EXT_BUTTON_MENU') -- ignore our stripped pseudo plugins
  AND    extjs_utils.auth_condition_check(condition_type,condition_expression1,condition_expression2,authorization_scheme) = 0
)  LOOP
  v_toolbarObj := JSON_LIST();
  --
  -- Lets loop through all our page buttons to create our region toolbar
  --
  FOR c1 IN
  ( SELECT pb.region_id
    ,      label
    ,      nvl(redirect_url,'apex.submit('''||button_name||''');') link
    ,      button_attributes
    ,      template
    FROM   apex_application_page_buttons pb
    ,      apex_application_temp_button  tmp
    ,      apex_applications             app
    WHERE  pb.application_id  = v_app_id
    AND    pb.page_id         = v_page_id
    AND    pb.region_id       = c.region_id
    AND    tmp.application_id = pb.application_id
    AND    app.application_id = pb.application_id
    AND    tmp.template_name  = pb.button_template
    AND    tmp.theme_number   = app.theme_number
    AND    extjs_utils.auth_condition_check(pb.condition_type,pb.condition_expression1,pb.condition_expression2,pb.authorization_scheme) = 0
    ORDER by pb.button_sequence
  ) LOOP
    --
    -- Lets check our button attributes for shortcuts
    --
    v_shortcut_name := regexp_replace(c1.button_attributes, '.*"(EXT_\w+)".*','\1');
    v_shortcut_name := CASE v_shortcut_name
                         WHEN c1.button_attributes THEN NULL
                         ELSE v_shortcut_name
                       END;
    IF v_shortcut_name IS NOT NULL THEN
 
      FOR c2 IN
      ( SELECT shortcut_name
        ,      shortcut
        FROM   apex_application_shortcuts
        WHERE  application_id = v_app_id
        AND    shortcut_name  = v_shortcut_name
      )  LOOP
        v_shortcut := c2.shortcut;
        --
        -- Lets loop through our shortcut and replace our menu references with a JSON object
        --
        WHILE instr(v_shortcut,'#MENU:') > 0 LOOP
          FOR c3 IN
          ( SELECT source_type
            ,      region_source menu_sql
            FROM   apex_application_page_regions
            WHERE  application_id   = v_app_id
            AND    page_id          = v_page_id
            AND    parent_region_id = c.region_id
            AND    source_type      = 'EXT_BUTTON_MENU'
          ) LOOP
            --
            -- Lets execute our query and return the result encoded in a JSON object
            --
            v_json := sql_to_json( p_sql => c3.menu_sql );
            v_shortcut := regexp_replace(v_shortcut,'(\$MENU:\w+\$)',v_json,1,1)..........

In the above we query all the regions on the page and ignore any of our pseudo plugin regions. We then build up the toolbar JSON object with the assistance of PLJSON and then loop through each of the buttons defined for the region and use the defined button template and make the necessary string replacements e.g. #BUTTON_ATTRIBUTES#. But before we do, we check the button attributes to see if a custom MENU shortcut has been defined and if so we extract the shortcut name and query the shortcut source. We then extract any MENU definition in the shortcut source and then in the c3 cursor loop we query “apex_application_page_regions” and check the source type matches our plugin name and we use this SQL definition and execute it to get the results for the named menu.

So what it allows us to do from a development point of view is define a single button template e.g. radio menu, and use our pseudo plugins SQL to define the menu items. Essentially our toolbar menu button is made up of a button template, shortcut, and plugin region. All three components can be subscribed and published across all applications in the workspace so we get the benfit of code centralization and any additional custom config for the button menu can be defined by either using multiple shortcuts or some additional config in #BUTTON_ATTRIBUTES#.

In order to make sure the plugin region never displays, we use a template named either “Ext.Exclude” which wraps the content in a DIV with style=”display:none;”. Optionally we can use another template named “Ext.Destroy” which uses the Ext.onReady function and removes the region/DIV from the DOM on page load. Both come in very handy in different situations. We simply ignore regions which have these templates assigned to them within our viewport generation code.

Note: you will see this error in the region source when you use a stripped plugin like we have

ORA-20100: No render function has been defined for plug-in PLUGIN_EXT_BUTTON_MENU

 

however this is never visible because the template we define either destroys the region or hides it from display.

Tagged with:
Jul 21

I’ve started doing some R&D with region plugins, one of the things I like is that we can define the “Region Source” as a SQL statement and set the minimum and maximum columns allowed. This means that we get builtin SQL statement parsing to make sure our queries are syntactically correct when we define our plugin query source, which is really nice. We can even show query examples so we the developer know what query structure is required when defining them.

Now on initial investigation one might think that you can have only one query associated with a plugin, and by definition that’s true, but have you considered creating a plugin that’s actually made up of say 5 plugin definitions? i.e. we have one parent plugin and 4 child plugins which work together to produce 1 result. Why would we want to do this? Consider a menu bar, we may want each menu e.g. “File”, “Edit”, “View” etc. to be defined from separate SQL queries. Since we only have one query definition that we can define per plugin, why don’t we just create one master plugin with X many child plugins. The child plugins will be stripped down, i.e. all they do is just define our query sources and with the option of setting a template to define what sort of component it is. We then use the APEX data dictionary within our parent plugin to locate the direct children during the rendering phase, execute them and use the results.

This approach can give us greater flexibility and visibility of our plugin definition as it’s visible and editable through the APEX IDE with a visual tree structure, thus ensuring we have less code and object maintenance on the backend. Potential other uses for a plugin with multiple query definitions could be grids within grids within grids. Anyway it’s early days for me I’m just tinkering around seeing what the possibilities could be. As I stated in an earlier post my favourite feature is the new parent/child setting for regions, it’s opened Pandora’s box in my eyes ;)

Tagged with:
Jul 19

All the APEX apps we build these days have are built with an EXT interface. Most APEX regions we define are mapped to an Ext.Panel or a variation of. As part of the integration we place the APEX region button section into an Ext  toolbar as a Panel allows the definition of a top and bottom toolbar. Now the cool thing about Ext is that these toolbars don’t have to be buttons, they can be menu’s, radio items, text fields, combo’s, sliders, spinners etc. 

Now the problem becomes how can we use an APEX button to display as a Slider for example? The short answer is, it requires a long answer ;)  

Now before I jump into the deep end and a detailed explanation, the point to note with our efforts is to always use a generic approach to integration. There’s no point having to hard code Ext config in too many places within the APEX IDE for each individual button. We like to centralize the maintenance of code changes. We achieve this by using templates, but sometimes a template is not enough. For this particular requirement of showing a slider in place of a button in a Panel’s top toolbar is one of these cases. 

 

Do you use shortcuts? Me personally, not very often until now….. it’s one of those APEX features I’ve never really tried to fully work out how to use properly, even though I’ve been developing with APEX for over 4 years now. After a little investigation my eyes have been opened to the usefulness of shortcuts for a couple of main reasons: 

  1. We can centralize the maintenance of them, like templates they can be subscribed to. This means that our master application which holds our theme can now also hold all our shortcuts and we can make one centralized change and push the changes out to all the subscribing applications.
  2. The creation and maintenance of them is handled by the APEX IDE, we don’t need to bolt on some additional custom maintenance screens for managing javascript template code. Either presented in runtime development mode or creating something similar to the APEX builder plugin. This speeds up our development.
  3. Because a shortcut is an APEX component we can query the APEX data dictionary, this means that we can easily build JSON metadata objects in the page header with these templates which will be used when transforming malformed JSON outputted by our APEX templates client side. This improves portability.

So how do we use shortcuts in our design? The design itself is a little complicated and is made up of a number of hacks ( I say hacks as it takes say 3 or 4 steps to do something that normally takes one step). It goes like this. 

We have a button template which looks like this: 

{
   "xtype": "slider",
   "listeners": {
      "change": {
         "fn": function (slider, newval, oldval) {
            var that = this;
            if (that.fnTimeout) clearTimeout(that.fnTimeout);
            that.fnTimeout = setTimeout(function () {
               var val = newval;
               #LINK#
            }, 1000);
         }
      }
   }#BUTTON_ATTRIBUTES#
}

This template design using the #LINK# substitution string allows to add custom javascript on a button by button basis to execute when the slider changes. We can manage the slider config by using the #BUTTON_ATTRIBUTES# field in the APEX IDE . Now this is fine, but the maintenance of having custom Ext config under #BUTTON_ATTRIBUTES# can become quite a headache, especially if we want to define a local data store or a significant amount of config. The text field in the APEX IDE is quite small so we find ourselves copying this to an editor and reading it/debugging it there. Normally most of these settings can be reused across multiple items, so in order to centralize the definition of this additional Ext config we use shortcuts. e.g. “EXT_BUTTON_SLIDER” is a shortcut which we’ve created in APEX… 

 

Now I bet your probably wondering (if I haven’t lost you already) how we could possibly use a shortcut in the #BUTTON_ATTRIBUTES# field as they are only supported in the following: 

  • The Region Source attribute of regions defined as HTML Text (with shortcuts). 

  • Region Header and Footer Text attribute. 

  • Item Label attributes and Default Value attribute. 

  • Region Templates attributes. 

  • The answer is at page generation time. We build a JSON metadata object in our page header by querying the APEX data dictionary which lists all our Ext button shortcuts, these shortcuts are snippets of Ext config which we’ll replace when building our toolbar. We use the name of the shortcut in the #BUTTON_ATTRIBUTES# section. 

    
    <script>
    
    const extShortcuts = {
       "EXT_BUTTON_COMBO": ",typeAhead: true,triggerAction: 'all',mode: 'local',store: new Ext.data.JsonStore({autoDestroy: true, id: 'extcombo-json', fields: ['D','d'], \"data\":[{\"D\":\"Display1\",\"d\":\"Return1\"},{\"D\":\"Display2\",\"d\":\"Return2\"},{\"D\":\"Display3\",\"d\":\"Return3\"}]}),valueField: 'd', displayField: 'D'",
       "EXT_BUTTON_SLIDER": ", \"width\": 100, \"minValue\": 0, \"maxValue\": 100",
       "EXT_BUTTON_RADIO_YESNO": ", \"items\": [{\"boxLabel\": \"Yes\", \"name\": \"radioyesno\", \"inputValue\": \"Y\"} ,{\"boxLabel\": \"No\", \"name\": \"radioyesno\", \"inputValue\": \"N\", \"checked\": true }], \"fieldLabel\": \"Radio\""
    }
    
    </script>
    

    Our region template looks like this (this is an extract of the region template): 

    
    <div id="regionButtons#REGION_ID#" style="display:none;">#CLOSE##PREVIOUS##NEXT##DELETE##EDIT##CHANGE##CREATE##CREATE2##EXPAND##COPY##HELP#</div>
    
    <script type="text/javascript">
    var tbar#REGION_ID# = Ext.app.cleanButtonJSON("regionButtons#REGION_ID#", "#REGION_ID#");
    Ext.onReady({
       Ext.app.apExtPanel("panel#REGION_ID#", "#REGION_ID#", tbar#REGION_ID#);
    });
    </script>
    

    From the above you can see that we call a function named “Ext.app.cleanButtonJSON”  before passing the toolbar object to our panel creation function as our JSON objects outputted by our custom templates are malformed (intentionally): 

    Ext.app.cleanButtonJSON = function (pJsonObj, pRegionId) {
       var buttons = new Array();
       Ext.select('[id=' + pJsonObj + ']').each(function (el) {
          buttons = el.dom.innerHTML.replace(/\$REGION_ID\$/g, pRegionId);
          for (var key in extShortcuts) {
             buttons = buttons.replace(new RegExp(key, 'g'), extShortcuts[key]);
          }
          buttons = eval("(" + "[" + new String(Ext.util.Format.stripTags(buttons)).replace(/\}\{/g, "\},\{") + "]" + ")");
       });
       if ((!buttons) || (buttons == undefined)) buttons = [];
       return buttons;
    }
    

    The above  javascript function  takes the innerHTML of the DIV which contains our buttons. The templates dictate that this is actually JSON and not HTML and we then “eval” it into an actual JSON object on page load. Before we perform the eval we replace our named shortcuts outputted by our templates, that’s why our shortcuts are printed out in an escaped string. This button object is then passed to our function which builds the Panel and adds the object to the “tbar” config parameter, and hey presto we have a slider in our toolbar. 

    Now the really cool part is that within our shortcuts that we create we define our own substitution strings, for example #LOV:EXT_BUTTON_LOV#. We do this because APEX actually never uses the shortcuts we only ever call them when we build our JSON metadata object in the page header, so we can define them however we like, which includes adding in our own substitution strings. Ok so take the example substitution string which I listed #LOV:EXT_BUTTON_LOV#. What we do with this is extract the LOV name and check the current page to see if an item exists e.g. P6_EXT_BUTTON_LOV, if it does we then extract the LOV definition otherwise we check the application level for a LOV named “EXT_BUTTON_LOV” and extract the query definition. e.g. code extract… 

    
    FOR c IN
    ( SELECT lov_definition lov_sql
      FROM   apex_application_page_items
      WHERE  application_id = v('APP_ID')
      AND    page_id = v('APP_PAGE_ID')
      AND    regexp_replace(item_name,'P\d+_','') = v_lov
      UNION ALL
      SELECT list_of_values_query lov_sql
      FROM   apex_application_lovs
      WHERE  application_id = v('APP_ID')
      AND    list_of_values_name = v_lov
    ) LOOP
    

    In either case we execute the SQL and encode the results into a JSON object which is replaced within our shortcut. e.g. #LOV:EXT_BUTTON_LOV# is replaced with the following JSON object 

     
    
    "data": [{
       "D": "Display1",
       "d": "Return1"
    },
    {
       "D": "Display2",
       "d": "Return2"
    },
    {
       "D": "Display3",
       "d": "Return3"
    }] 
    

    This means that our combo’s can have a consistent definition but different data stores .i.e. on page 6 we have a list of people but on page 7 we have a list of customers, the beauty is that we use the exact same shortcut and thus reducing the amount of code required 

    Here’s an example of the shortcut with the substitution string 

    Ok so you’re probably thinking that the above seems over complicated and too many hacky steps , but it’s the end result we’re interested in and the speed of development. Now that we’ve created this structure adding in a combo or slider or other custom elements is as quick as creating a button in the APEX IDE and selecting the appropriate template. So APEX development productivity is not impacted. Plus we have the ability to embed whatever  javascript we want executed when a user change event occurs. If anything we are boosting productivity by being easily able to define widgets instead of buttons within a region button bar.  The argument for using  this implementation Vs a region plugin is that this fits better into our overall framework design and is reused by all our region templates. 

    If you’re still left scratching your head or thinking that this doesn’t really apply to you, take a step back and think about how flexible APEX is as a development tool. You can basically bend the product to meet your own requirements. It’s a blank canvas and gives you all the tools you need.

    preload preload preload