APEX and Asynchronous Ajax

I rarely read the release notes because I’m in a hurry to get to the good stuff. APEX 5.0 has plenty of good stuff to get to but I want to call your attention to one release note in particular: “Deprecated Synchronous Ajax in Dynamic Actions”

Earlier this year there was an interesting thread on the APEX forums titled Synchronous requests deprecated. It points out that some browsers are now giving warnings (if you have the console open) when synchronous ajax requests are made and, more importantly, that APEX apps that use dynamic actions with Wait For Result set to Yes will make a synchronous request.

This APEX 5.0 release note is letting you know that in the future APEX will be fixed so that dynamic actions will make asynchronous ajax requests. So check now to make sure your apps do not depend on the current synchronous behavior (and tell your friends). In the above thread I describe a couple of situations to avoid that depend on synchronous behavior.

The purpose of this post is to provide an example of what to do if you can’t wait for the future and need to make asynchronous requests today.

I created a demo app that shows the difference between synchronous and asynchronous ajax. To following along you will need to download the demo and import it into APEX 5.0. Note: the techniques provided here should also work with minor adjustments in APEX 4.2.

The app does the same thing two different ways; sync and async. Run the app and try the sync version first. Select an event, enter a number and press the Check Availability button. This uses an Execute PL/SQL Code dynamic action that simulates a process that takes a long time and then a JavaScript action to update the UI. The PL/SQL code pauses for 5 seconds. Notice that the progress spinner is never seen and that the UI is frozen during the request (for example clicking on the select list will not drop it down).

Next try the async version. Notice that the UI is not frozen and the progress spinner is visible and you can tell that the button is disabled during the request.

To turn the sync version into an equivalent async version do the following:

  1. Copy the code from the Execute PL/SQL Code action into a new Ajax Callback PL/SQL Code process. Give the process a meaningful name. The demo uses name “CHECK_AVAIL”. In a later step you will add code to call this process using JavaScript and you will need this name. This is the code copied (for this demo the page item names are updated because it is on a different page):
    
    -- simulate checking for availability of a given quantity of tickets for a given event
    -- where it could take a while to get a result
    wwv_flow_utilities.pause(5);
    if :P3_EVENT = 'E2' then
        :P3_AVAIL := 'Sold Out';
    elsif :P3_QUANTITY > 8 then
        :P3_AVAIL := 'Not enough seats available';
    else
        :P3_AVAIL := 'OK';
    end if;
    
  2. Add code to the Ajax Callback process to return any needed page items. One very nice thing that PL/SQL dynamic actions do is let you specify the page items to submit and page items to return. The current value of the page items to submit are read and put into the ajax request and are available as bind variables in the PL/SQL code block. The page items to return are bind variables that you assign to and then are returned in the ajax response and the page item current value is updated. Ajax Callback processes don’t let you declare page items to return. This is because these processes are general purpose and let you return anything you want. The demo returns a single page item value. Here is the added code:
    
    -- use the new apex json api to return a list of page items to set
    -- this would need to be adjusted for APEX 4.2
    apex_json.open_object;
    apex_json.open_array('item');
    apex_json.open_object;
    apex_json.write('id', 'P3_AVAIL');
    apex_json.write('value', :P3_AVAIL);
    apex_json.close_object;
    apex_json.close_array;
    apex_json.close_object;
    

    This code uses the new APEX 5.0 JSON API. If you are using APEX 4.2 you will need to replace this with code to generate a similar response using the APEX_JAVASCRIPT API. It is possible to use any format you like as long as you have code on the client side that can understand it. If your dynamic action doesn’t have any page items to return then you can skip adding this code.

  3. Replace the dynamic action Execute PL/SQL Code action and any following actions with a single JavaScript action. Actions before the Execute PL/SQL Code action can be left as is. The following actions are the ones that were waiting for the results of the PL/SQL Code. If the following actions are JavaScript then just save that code to a temporary place. Any other action types such as Enable or Show will need to be converted to equivalent JavaScript. As a side note the only Dynamic Action you really need is the JavaScript action. I hardly ever use any of the others except for Execute PL/SQL Code when I don’t need to wait for the result. Here is the new replacement JavaScript action:
    
    // invoke a PL/SQL process on the server by making an ajax request
    var jqxhr = apex.server.process("CHECK_AVAIL", {
            pageItems:["P3_EVENT", "P3_QUANTITY"] // these are the page items to submit
        }, {
            loadingIndicator: "#P3_AVAIL",
            loadingIndicatorPosition: "append"
        });
    // the above call returns right away but the done method of the jqxhr deferred object will call 
    // our function when the request is complete
    jqxhr.done(function(data) {
        var i, item, avail;
        // data could be anything you like but in this example do what a DA would do
        // this handles the page items to return set by the server process
        // data result is object with property item which is an array of page item id, value pairs
        if (data && data.item) {
            for (i = 0; i < data.item.length; i++) {
                item = data.item[i];
                $s(item.id, item.value, null, true );
            }
        }
    
        // anything that would have been a dynamic action after the PL/SQL dynamic action goes here
        // this was the JavaScript action
        avail = $v("P3_AVAIL");
        if (avail === "OK") {
            $("#P3_AVAIL,#P3_QUANTITY_LABEL").addClass("quantityOK");
        } else {
            $("#P3_AVAIL,#P3_QUANTITY_LABEL").addClass("soldOut");
        }
        // this was the enable button action - gave the button a static id to make this easier
        apex.item("checkBtn").enable();
    })
    

    This code can be broken down into 3 parts. Part 1 is a call to the CHECK_AVAIL process created in step 1 using the apex.server.process API. This API makes it easy to submit page items just like the previous PL/SQL action did. The function apex.server.process initiates the ajax request to the server but doesn't wait for a response. Instead it returns a deferred object which is assigned to variable jqxhr. At this point the dynamic action event handler is done. When the request is complete the function passed to the done method of the deferred object is called. The second and third parts happen inside this function once the request is complete.

    Part 2 is processing the page items returned. The generic code used here will work for any page items if they are returned from the process in the same way. It sets the page item value to what the server returned.

    Part 3 is all the original following actions. In this case you can see that the JavaScript code is the same. The Enable action has been translated to the equivalent apex.item API method.

That's all there is to it. Admittedly it is more complicated than the dynamic action only solution. If the synchronous ajax isn't currently bothering you then leave well enough alone and know that as long as you don't rely on the synchronous behavior your app should keep working without change when dynamic actions are changed to be asynchronous.

This technique uses documented APEX APIs so it will continue to work even after dynamic actions change. However you may want to switch back to the pure dynamic action method because it is simpler.

If nothing else this should give you a greater appreciation of all the work that APEX dynamic actions do for you. But knowing this technique opens up additional possibilities that can't be done with dynamic actions alone. For example what if you wanted to make 3 ajax requests in parallel and then after all 3 have finished do some client side processing. The apex.server API along with jQuery deferreds will allow you to do that.

One thought on “APEX and Asynchronous Ajax

Comments are closed.