Open modal page using Pretius APEX Context Menu

I have received multiple questions on how to make the plugin Pretius APEX Context Menu work with reports rows. In particular, the most often question is how to open a modal page with a value from a report row.

For that case scenario, I have prepared an example demo page at apex.oracle.com. This article describes step by step implementation.

Working demo at apex.oracle.com

Preliminary assumptions

I don’t want to describe the basics of APEX such as creating modal page with items so this instruction covers:

  • creating employee report with required configuration based on emp table,
  • creating simple APEX list for a popup menu (the plugin Pretius APEX Context Menu),
  • the plugin implementation for an icon in 1st report column,
  • the plugin implementation for right-clicking on report cells (context menu)

I assume:

  • you have installed the plugin Pretius APEX Context Menu in your application,
  • the page containing report Employees is identified by ID 1120,
  • the modal page is identified by ID 1121 and it contains APEX item named P1121_EMPNO.

Native approach

John Snyders already described how you can create a custom menu for a report by implementing the native APEX popup menu widget API. In his article from 2015, he described the custom menu opening modal page with the value passed from a report row. The article is 4 years old and it is still the valid approach to open modal page (it can be implemented in APEX 5.x, 18.x and 19.x.)

(…)

The reason I choose to include a dialog page in this example, other than they are an awesome new 5.0 feature, is because they present a particular challenge. The URL to a dialog must be generated on the server using the PREPARE_URL function (this is so that a checksum can be calculated to protect against click-jacking). But we need the dialog URL to be specific to the report row. If you try to use the apex.navigation.dialog API with a URL created on the client you will find that the dialog page doesnโ€™t open because the checksum must be generated by APEX_UTIL.PREPARE_URL. When you use APEX_UTIL.PREPARE_URL for a page that is a dialog you will see that you get back a javascript url. That is fine if the URL is going into an anchor href or an onclick attribute but if the URL is sent to the client as a string it is useless unless you eval it and that is not a good idea. One option is to wrap the generated URL (which is really just code) in a function in a script tag but doing that in the context of a report, list or tree would not be easy. The other option used here is to strip away everything but the URL from the output of PREPARE_URL and send it to the browser in a data attribute. In this way we can call apex.navigate.dialog with a URL that has a valid checksum.

(…)

John Snyders @ APEX 5.0 Custom Menus, 2015

John is describing the obstacle on how to evaluate the generated URL without taking a risk of using evil eval function. Normally the output of APEX_PAGE.GET_URL (alternative for APEX_UTIL.PREPARE_URL) for APEX modal page looks like this:

javascript:apex.navigation.dialog('f?p=131842:1121:708969859638519:EDIT:NO:1121:P1121_EMPNO:7839\u0026p_dialog_cs=rwCgeLDSU5QMzlZG43xvON3jokDhhkrjoWoMtrcyjLPW3Aeub5mONo6tzFNwxvlv-BjkkY7MpGo9PlupnknS9A',{title:'Edit employee',height:'603',width:'720',maxWidth:'960',modal:true,dialog:null},'t-Dialog-page--standard '+'',apex.jQuery('#R75103274143723269701'));

It is ready to be executed JavaScript code which will open a modal page with defined modal page attributes (title, width, height and others). When this code is embedded in an anchor href or onclick attribute it is being executed in the context of the browser window. This is perfectly fine because :

  • any change in URL item value will cause checksum error,
  • every user is able to inject and execute JavaScript code in the context of the browser window.

To make it work with custom APEX popup menu John suggests stripping the URL from JavaScript code so it can be used as the parameter for native APEX JavaScript function –apex.navigation.dialog. The result of such stripping is presented below:

f?p=76842:3:110134127004527::NO::P3_DEPTNO:10\u0026cs=3gWUQkEyQt2vieWSc8ESVQkrOly96OpRdq8LL-RKYyMCoW5uiDYd2O_ux6xtMkp2rl0ATadV74Glam4gwszc7wg\u0026p_dialog_cs=owYBwRH1i7kz0AnAuwMFOrXXS7LQCh0g9LSgch7URSz8MJis0IkNKgdkllXPhJUwJovVc_axLMzdzPD9upWIiA

The checksum is still there, an application remains safe but you lose information about the modal page-attributes. John’s approach is valid but it is a little bit inconvenient because

  • it requires stripping JavaScript from URL,
  • it required writing your own implementation of the APEX popup menu,
  • developer loses information about modal page attributes – as John wrote it can be done with workaround but it is extra work

I want to show you a different approach that:

  • respects checksum generated by APEX_PAGE.GET_URL,
  • don’t need to parse URL or evaluate generated JavaScript code using evil eval function,
  • don’t lose information about the modal page attributes.

Additionally, thanks to the plugin it:

  • uses defined APEX list, even if part of it will be dynamic,
  • respects Authorization Schemes to determine user privilege to each APEX list menu entry,
  • allows extending defined menu entries with dynamic submenus based on current data in a report row.

The approach is as simple as possible and what is most important, it is secure. Just embed the generated URL in the onclick attribute of hidden anchor and then override list entry to trigger click event on hidden anchor.

(…) That is fine if the URL is going into an anchor href or an onclick attribute but if the URL is sent to the client as a string it is useless unless you eval it and that is not a good idea. (…)

John Snyders @ APEX 5.0 Custom Menus, 2015

The solution can be combined with John’s approach but if you want to learn how to make it work with my plugin then keep on reading.

Create the report

On page 1120 create Classic Report named Employees with Source \ SQL Query defined as

select
  EMPNO,
  ENAME,
  HIREDATE,
  JOB,
  MGR,
  SAL,
  COMM,
  DEPTNO,
  APEX_PAGE.GET_URL (
    p_application        => :APP_ID,
    p_page               => 1121,
    p_session            => :APP_SESSION,
    p_request            => 'EDIT',
    p_debug              => :DEBUG,
    p_clear_cache        => 1121,
    p_items              => 'P1121_EMPNO',
    p_values             => EMPNO
  ) as EDIT_URL
from
  emp

Column EDIT_URL will contain generated JavaScript code to open modal page and it will be embedded as an onclick attribute of the hidden anchor. Now, customize the report so you will have an icon and hidden anchor in the first column:

  1. hide column EDIT_URL
  2. Create a Virtual Column.
    1. Move it to 1st position
    2. Set Identification / Type to Plain Text
    3. Set Column Formatting / HTML Expression to
<a href="javascript: void(0)" class="row-menu">
  <span class="fa fa-navicon"></span>
</a>
<a href="javascript: void(0)" onclick="#EDIT_URL#" class="modal-anchor">Open Modal Page</a>

It will display an anchor with an icon that will be used as the plugin triggering element. The anchor with class modal-anchor will be hidden thanks to adding CSS rule in Page Attributes \ CSS \ Inline

.modal-anchor {
  display: none;
}

Save and run the page and you should see Classic Report on emp table with the icon in first column.

Create the APEX list for the popup menu

Navigate to Application \ Shared Components \ Lists and create a new static list named EMP_CONTEXTMENU with 4 entries with the target set to

javascript: void(0);

It should look like:

Oracle APEX List “EMP_CONTEXTMENU” defnition

Now, you need to configure the entries so

  • entry Edit will be ready to be extended by the plugin
  • (optional) entry Separator will be displayed as separator
  • (optional) entries Action 1 and Action 2 will be disabled – the popup menu purpose is to aggregate actions and for demo purpose, it looks better when you have more than 1 entry ๐Ÿ˜‰

Entry “Edit”

Edit the entry and set its attributes as follows:

  1. Set Entry \ Image/Class to fa-pencil
  2. Set User Defined Attributes \ Attribute 1 to EDIT

Entry “Separator”

Edit the entry and set User Defined Attributes \ Attribute 7 to separator. The plugin based on this information will display this entry as the popup menu entry separator.

Entries “Action 1” and “Action 2”

Edit entries, pick up the icons (fa-star-o and fa-apex) and set User Defined Attributes \ Attribute 2 to true. The plugin based on this information will display those entries as always disabled.

Reminder about the plugin integration with APEX list

If you want to learn what attributes are used by the plugin please read the documentation at the plugin GitHub repository.

GitHub documentation on the plugin integration with Oracle APEX List

Implement the plugin

You have the report and APEX list ready. Now implement the plugin so you will have the popup menu appear when a user:

  • clicks on the icon in the first column,
  • right-clicks on report cells (but not in first column).

In both scenarios, you have to implement simple dynamic action.

Icon popup menu

  1. Create new Dynamic Action named Context menu | icon menu
  2. Set When \ Event to After Refresh
  3. Set When \ Selection Type to Region
  4. Set When \ Region to Employees
  5. Set Advanced \ Event Scope to Static

Change default true action from Show to Pretius APEX Context Menu [Plug-in] and:

  1. Set Settings \ List name to EMP_CONTEXTMENU
  2. Uncheck Settings \ Settings \ Stop Event Propagation
  3. Select Settings \ Settings \ Narrow to Affected Elements
  4. Set Affected Elements \ Selection Type to jQuery Selector
  5. Set Affected Elements \ jQuery Selector to .row-menu (class of icon anchor)
  6. Set Execution Options \ Fire on Initialization to Yes
Definition of dynamic action “Context menu | icon menu”

Report cell context menu

Right-click on cells should open a popup menu. For sake of learning let’s make it appear at the current mouse position – X and Y.

  1. Create new Dynamic Action named Context menu | td right-click
  2. Set When \ Event to Custom
  3. Set When \ Custom Event to contextmenu
  4. Set When \ Selection Type to Region
  5. Set When \ Region to Employees
  6. Set Advanced \ Event Scope to Static

Change default true action from Show to Pretius APEX Context Menu [Plug-in] and:

  1. Set Settings \ List name to EMP_CONTEXTMENU
  2. Select Settings \ Settings \ Display at Mouse Position
  3. Select Settings \ Settings \ Narrow to Affected Elements
  4. Select Settings \ Settings \ Stop Event Propagation
  5. Set Affected Elements \ Selection Type to jQuery Selector
  6. Set Affected Elements \ jQuery Selector to tr td:not(:first-child)
  7. Set Execution Options \ Fire on Initialization to No
Definition of dynamic action “Context menu | td right-click”

At this point clicking on popup menu entry Edit does nothing but in a couple of minutes you will open the modal page with the value of EMPNO passed to the modal page item P1121_EMPNO.

Classic Report with the plugin implemented – without working actions

Override Edit entry behavior

The plugin main functionality is the possibility to override (at the runtime) the behavior of already defined APEX list entries. In this use-case scenario, you will be overriding the action function of the popup menu entry.

Icon popup menu

  1. Edit true action of dynamic action Context menu | icon menu
  2. Select Settings \ Settings \ Override Behaviour
  3. Set Settings \ Override Behaviour to
return {
  "EDIT": {
    "action": function( pMenuOptions, pTriggeringElement ){
      var anchor = $(pTriggeringElement);
      anchor.closest('td').find('.modal-anchor').trigger('click');
      return void(0);
    }      
  }
};

Code above returns JSON object that informs the plugin to override entry with ID defined as EDIT in APEX list EMP_CONTEXTMENU. In particular, it overrides the action function which is executed when a user clicks on the popup menu Edit entry. Using the 2nd parameter (pTriggeringElement) it recognizes the triggering element (icon anchor) and then performs click event on the hidden anchor which results in opening the modal page.

Reminder about inline help text for the plugin attributes

Keep in mind that I have prepared inline help text for all the plugin attributes in Page Designer.

Pretius APEX Context Menu – Inline-help text

Report cell context menu

It is a little bit different story when it comes to a popup menu displayed at the mouse current position (X and Y) – the plugin attribute Settings \ Display at Mouse Position is checked.

The reason for this is simple. A popup menu opened for mouse X and Y (not aligned with the given jQuery object) is not opened in the context of the triggering element – the second parameter of action function is always null.

This option doesn’t apply to html and APEX defined buttons!
When checked, menu is positioned using mouse current X and Y coordinates. Second parameter for action function is set to null and can’t be used to reference invoking DOM element. To reference DOM element invoking menu use $.proxy method which will override context of entry action function.

Inline Help Text in Page Designer for the plugin attribute Display at Mouse Position

So, you have to override the context of action function using jQuery.proxy!

  1. Edit true action of dynamic action Context menu | td right-click
  2. Select Settings \ Settings \ Override Behaviour
  3. Set Settings \ Override Behaviour to
return {
  "EDIT": {
    "action": $.proxy(function( pMenuOptions, pTriggeringElement ){
      //pTriggeringElement is still null
      var td = this;
      td.closest('tr').find('.modal-anchor').trigger('click');
      return void(0);
    }, this.element)
  }
};

Again, from the plugin inline help-text for attribute Override Behaviour, you can learn that I’ve provided extra variables that can be accessed via this (same as this in Execute JavaScript Code true action). In this case, you will use this.element which is described as:

A jQuery reference to the DOM object of the element that invoked a menu.

Inline Help Text in Page Designer for the plugin attribute Override Behaviour

It means that action function has a new execution context set to triggering element (which is a report cell) and you can access it using this reference in the function body. Thanks to this you are again able to find hidden anchor responsible for opening the modal page and perform an event click on hidden anchor.

Now, save and run the page. Your result should be similar to the example demo page, where clicking on entry Edit in a popup menu opens the modal page with item P1121_EMPNO value set to the EMPNO value from a row.

Working demo at apex.oracle.com

Ps.
If you like the plugin and you want to help me promote it, please give it a like at apex.world. It really helps community members to find valuable plugins.

apex.world the plugin Pretius APEX Context Menu page

If you want to help me even more, please consider reading page Support me.

Ostrowski Bartosz

Oracle APEX Developer @PretiusSoftware

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *