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.

    Jul 16

    Just a quick post, I was having a quick look at one of our customer applications which we developed as we’ve been reviewing a number of requested changes. I stumbled onto an interesting statistic: one of the data entry screens has had over 917, 076 records manually entered through it over a 2 year period. An additional 121 havce been added whilst I’ve  been writing this post.

    To give you a size of the application it’s made up of 165 pages and has a user base of over 7000 people  (there are no data load processes in place). If you think that it’s probably some simple arbitrary form, there are 92 items defined on the page, with some pretty heavy javascript sitting behind the scenes. I would think it’s fair to say that APEX is doing a good job and is pretty important to this company.

    Jul 16

    We’ve bitten the bullet and updated our internal development environments to APEX 4.0 which includes our APEX/Ext development framework. We had no troubles during installation. Post Installation, there have only been a few minor issues which we’ve run into which are:

    1. Changing our javascript override for “doSubmit” to “apex.submit”, we use this for upload/downloading multiple clobs and other things….
    2. Catering for data dictionary changes for item type “Display As” definitions
    3. Viewing session state: collection information does not appear to work
    4. We’ve had to move the developer toolbar on page load using javascript (i.e in runtime development mode) as it sometimes overlays important navigation on the page e.g. bottom tabs on a tabpanel etc.
    5. That’s it!!

    APEX 4.0 is a big jump for the product and personally I must say that the development team has done a fantastic job as we were quite worried about the impact this version would have on all the previous custom development we’d performed against earlier versions. We can breathe easy now ;)  

    What’s my favourite part/new feature? Plugins come a close second but my favourite new feature is the ability to create a region within a region. Now you’re probably thinking that I’ve got a couple of screws loose! but for me this single feature has just opened the door for a perfect design for APEX and Ext integration, as we can now build/define page components/templates in the exact same form Ext creates them, e.g. Viewport -> Layout -> Panel -> Form e.g.

     

    Note: the above is just a quick mockup as all regions are defined as “HTML”, normally the region types would reflect their specific type e.g. a Tree would be a Tree, a Form would be a Form etc.

    I am a little biased though on the features that I like because I’m purely focused on APEX and Ext, but I’m also spicing things up by using a sprinkling of jQuery here and there, so in the future we’ll be documenting the sociability of APEX/Ext/jQuery, as we’d like to still use some of the productivity gains we get from dynamic actions and the likes.

    Finally we’re curious if anyone would purchase a license and support for a commercial plugin, we’re considering building an APEX 4.0 plugin for the Ext Gantt demo with full editing support. Just leave a comment to start a discussion on the (controversial) topic…

    Tagged with:
    Jul 06

    I love the flexibility of Javascript and how it’s easy to overwrite existing code either by reusing the same function name or using framework wrappers like Ext.override. We use custom APEX templates and centralized generic PLSQL packages to integrate Ext widgets into APEX, the downside is that these packages can be too generic, i.e. for a Tree widget we need to handle specific actions like drag and drop re-ordering of the tree nodes and saving these changes to the database. Now in some instances we may base the tree data on a non-updateable view as we may want to disable the ability to reorder certain parts of the tree, so we use a UNION style query with some additional config to pass through when creating the Ext JS tree

    e.g.

      CREATE OR REPLACE FORCE VIEW PORTAL_LINKS_NAV_VW
      ("ID", "PID", "NAME", "LINK", "A1", "A2") AS
      SELECT f.form_id                                                   id
      ,      '12345'                                                     pid
      ,      f.form_name                                                 name
      ,      'f?p=&APP_ID.:SSO:&APP_SESSION.::::P30_FORM_ID:'||f.form_id link
      ,      extjs_utils.asciiEscape(flex_01)                            a1
      ,      '{ "allowDrag": false, "allowDrop": false }'                a2
      FROM   portal_sso_forms f
      WHERE  f.enabled = 'Y'
      UNION
      SELECT '12345'
      ,      NULL
      ,      'Single Sign On'
      ,      NULL
      ,      NULL
      ,      '{ "allowDrag": false, "allowDrop": false }'
      FROM   dual
      UNION
      SELECT to_char(link_id) id
      ,      to_char(nvl(parent_link_id,54321)) pid
      ,      link_name name
      ,      link_url link
      ,      NULL a1
      ,      NULL a2
      FROM   portal_nav_links
      WHERE  user_id = portal_security.get_user_id(v('APP_USER'))
      OR     user_id   IS NULL
      UNION
      SELECT '54321'
      ,      NULL
      ,      'General Links'
      ,      NULL
      ,      NULL
      ,      NULL
      FROM   dual
      ORDER BY 3;
    

    Normaly we query the APEX data dictionary and use DBMS_SQL to describe the tree query which gives us all the information we need to update the base table data for a reordering event. However we can see from the above query definition there is no way we could update the view (well technically we could use an instead of trigger but that defeats the purpose of this post). So we need some custom functionality to handle a tree re-order event. Instead of writing a complete separate custom widget simply for this purpose what we did was when the widget re-order event came through our widget handler we performed a lookup using the database data dictionary to check for a PLSQL override

    e.g.

      PROCEDURE widget
      ( p_widget_name         IN VARCHAR2 DEFAULT NULL
      , p_widget_mod          IN VARCHAR2 DEFAULT NULL
      , p_widget_action       IN VARCHAR2 DEFAULT NULL
      , p_widget_action_mod   IN VARCHAR2 DEFAULT NULL
      , p_widget_num_return   IN VARCHAR2 DEFAULT NULL
      , x01                   IN VARCHAR2 DEFAULT NULL
      , x02                   IN VARCHAR2 DEFAULT NULL
      , x03                   IN VARCHAR2 DEFAULT NULL
      , x04                   IN VARCHAR2 DEFAULT NULL
      , x05                   IN VARCHAR2 DEFAULT NULL
      , x06                   IN VARCHAR2 DEFAULT NULL
      , x07                   IN VARCHAR2 DEFAULT NULL
      , x08                   IN VARCHAR2 DEFAULT NULL
      , x09                   IN VARCHAR2 DEFAULT NULL
      , x10                   IN VARCHAR2 DEFAULT NULL
      ) AS
    
        l_exists BOOLEAN DEFAULT FALSE;
        l_proc VARCHAR2(200);
    
      BEGIN
        CASE p_widget_name
         WHEN 'Ext.tree.ajax.reorder' THEN
            --
            -- We will check to see if there is an override procedure to execute
            --
            FOR c IN
            ( SELECT *
              FROM   user_procedures
              WHERE  object_name    = 'EXT_PLSQL_OVERRIDES'
              AND    procedure_name = 'TREE_REORDER_'||p_widget_mod
            ) LOOP
              l_exists := TRUE;
              l_proc := c.object_name ||'.'|| c.procedure_name;
            END LOOP;
            IF l_exists THEN
              EXECUTE IMMEDIATE 'BEGIN '||l_proc||'(:b1,:b2); END;' USING x01, x02;
              htp.p('{ "message": "The node was updated successfully!", "success": true}');
            ELSE
              extjs_widgets.tree_reorder
              ( p_widget_name         => p_widget_name
              , p_widget_mod          => p_widget_mod
              , p_widget_action       => p_widget_action
              , p_widget_action_mod   => p_widget_action_mod
              , p_widget_num_return   => p_widget_num_return
              , x01                   => x01
              , x02                   => x02
              , x03                   => x03
              , x04                   => x04
              , x05                   => x05
              , x06                   => x06
              , x07                   => x07
              , x08                   => x08
              , x09                   => x09
              , x10                   => x10
              );
            END IF;
        ELSE
          .....
    
        END CASE;
      END widget;
    
    create or replace
    PACKAGE BODY EXT_PLSQL_OVERRIDES AS
      PROCEDURE tree_reorder_18897225474833114
      ( p_node_id       IN VARCHAR2
      , p_new_parent_id IN VARCHAR2)
       AS
      BEGIN
    
        UPDATE portal_nav_links
        SET    parent_link_id = p_new_parent_id
        WHERE  link_id        = p_node_id;
        COMMIT;
    
      EXCEPTION
        WHEN OTHERS THEN
          -- Custom exception logging code goes here
          -- e.g. logger.log(SQLERRM,'EXT_PLSQL_OVERRIDES.tree_reorder_18897225474833114');
          htp.p('{"success":false, "message": "'||extjs_utils.asciiEscape(sqlerrm)||'"');
    
      END tree_reorder_18897225474833114;
    
    END EXT_PLSQL_OVERRIDES;
    

    So what this allows us to do is cater for specific functionality for generic widgets. We basically code the ability for the developer to override any generic functionality we declare in PLSQL (Note: our PLSQL packages use “AUTHID CURRENT_USER” so when we check for the presence of the overrides package it will be in the APEX application parsing schema). The only downside from the above approach is that we hard code region id’s. This means that we have to retain the APPLICATION_ID for the lifetime of the application. To workaround this issue we use a high range of application id’s which we don’t expect to be reused by anyone (we hope).

    The concept above is not to really aimed to illustrate our integration of Ext and APEX but more so to show you the benefit you can get by using a combination of the Database Data Dictionary and APEX Data Dictionary. Combining both will allow you to create some pretty cool dynamic functionality.

    preload preload preload