I wanted to use Dynamic Actions to create a modal pop-up dialog of APEX-pages, and this is what I came up with. It is not as streamlined as I would have liked it to be, but a big leap in the right direction nonetheless.
Demo Application
I have created a demo application which you can test here: http://apex.oracle.com/pls/apex/f?p=45420:2. You can also download the application here: http://apex.oracle.com/pls/apex/f?p=45410:DOWNLOAD.
Identifying Triggering Elements
The trick with the Dynamic Actions is again to identify which elements you would like to use to trigger the dialog. In my example I have a plain SQL-report, but I have edited the primary key column to display an edit icon, and under the Column Link properties, I have set Link Attributes to include id="callModalDialog#EMPNO#". This gives me the possibility to identify the links using a JQuery selector expression.
I created a Dynamic Action on the click event, with Selection Type JQuery Selector with value a[id^=callModalDialog]. This will identfy all items with attribute id starting with callModalDialog, which, incidentally, is the same id I used on my edit links in the report.
To ensure that the event triggers after refresh of the report, I set the Event Scope to live under Advanced (as opposed to bind, which is normal). This saves me the effort of re-binding the click-events when the report is refreshed.
Calling the Dialog
When the user clicks the edit links, I want to do two things: suppress the default browser behavior, and call the modal dialog. I achieve this by adding a true Action of type Execute Javascript Code.
This contains the javascript code to do both the things I want to do:
/* prevent default behavior on click */ var e = this.browserEvent; e.preventDefault(); /* Trigger JQuery UI dialog */ var horizontalPadding = 30; var verticalPadding = 30; $('<iframe id="modalDialog" src="' + this.triggeringElement.href + '" />').dialog({ title: "Edit Employee", autoOpen: true, width: 700, height: 300, modal: true, close: function(event, ui) {apex.event.trigger('#P2_AFTER_MODAL','select',''); $(this).remove();}, overlay: { opacity: 0.5, background: "black"} }).width(700 - horizontalPadding).height(300 - verticalPadding); return false;Some explanation for this is in order. The first part is just to prevent the default browser behavior (user click a ink, and the browser naturally wants to open that page). The second part is by far more complex, and contains all the code needed to start the JQuery UI dialog.
The edit link points to an APEX application page, and the code above invokes the page in an IFRAME, and uses the JQuery UI dialog to show the IFRAME inline in a modal dialog. I extract the link from the triggeringElement, set the title (this could also have been extracted from the triggeringElement), set various attributes for the dialog and IFRAME. In short the code above says open the HREF of the triggeringElement in an IFRAME, show the IFRAME in a modal dialog and open the dialog immediately.
Returning from the Dialog
If you notice the code above, the close configuration for the dialog looks quite complex. The reason for this is that I want to execute a Dynamic Action when the dialog is closed. There are two problems with this; (as far as I know) I cannot execute a Dynamic Action directly from Javascript, and I have nowhere to attach the Dynamic Event anyways. Write som javascript function you say? Yes I could do that, but the Dynamic Action is too good to pass up. Let us say that I want to refresh a report, set some item values and execute some PL/SQL when returning, with Dynamic Actions that is a walk in the park.
This is where I cheat :-)
The dialog does not exist when the page renders, so I have nowhere to attach my Dynamic Action to execute when returning from the dialog. My (kludgy) solution to this was to create a hidden item in the report region, and attach the Dynamic Action to the item. This way I can see all that happens in the Builder (easier to maintain), and if I give it sensible names, I can even discern what it does. In my case I created an item called P2_AFTER_MODAL, and attached a Dynamic Action called Refresh Report.
So, now I have defined what to do after returning, but how to trigger the event? And I cheat again... I attach the Dynamic Event to P2_AFTER_MODAL with the select event. The select event on a hidden item does not get triggered very easily. In effect this gives me a way to control that the Dynamic Action only executes when I programmatically tells it to. That is exactly what I tell it to when closing the dialog, using the APEX javascript API apex.event.trigger.
I also destroy the dialog after use, so I can create a new on the fly when the user wants to edit another employee, $(this).remove(); takes care of that (removing the IFRAME in effect, I could not get the dialog.destroy-method to work properly).
Closing the Dialog
When creating an APEX page that is to be a pop-up window, you need to take special care on the branching. In this case, you want to take care to close the dialog, both on cancel and after processing.
To handle cancel, let the cancel button redirect to URL, and set the URL target to: javascript:parent.$('#modalDialog').dialog('close');.
Notice the use of parent, the dialog is spawned by the original web page, and the dialog and its method belongs to the parent.
When branching after processing you have to be a bit more creative, create an unconditional branch to PL/SQL Procedure that runs the following code:
BEGIN --closes this popup window htp.p('<body>'); htp.p('<script type="text/javascript">'); htp.p('parent.$(''#modalDialog'').dialog(''close'');'); htp.p('</script>'); htp.p('</body>'); END;This is in effect the same as the URL target behind the cancel button, but rendered by htp.p in PL/SQL.
Layout of the Dialog Page
In the sample application have have copied the Printer Friendly template, called it Modal Dialog, and used that as a page template for the dialog page. The modifications is basically just removing the navbar, and overriding the default "min-height" and "min-width" css attributes by explicitly setting style-attributes for the body-tag and div with id="body". This will vary, depending on the theme you are using. The goal is to strip the dialog of all unnecessary layout clutter.
Some Notes
I have created a few modal dialogs in the previous versions of APEX, and the javascripting involved is a pain. Maintaining the code even more painful. Even with the simple trick (or cheat, if you will) of attaching the Dynamic Action to an item in a declarative way, helps me getting control over the classic "what happens where, when and why".
Even though the basics is demonstrated in the sample application, there is a bit of work left. When you return from the dialog and refresh the report, you might want to take care to maintain the pagination, and if the user closes the dialog with cancel or window close, you might not want to refresh the report, etc., etc.
Enjoy :-)