Wednesday, March 20, 2013

Ajax calls in apex: examples

Just an example on the various ways to perform an ajax call in apex.

Setup:

I'm doing this on page 18. You can do this on any page of course, if you just adjust the page item name.
There is a tabular form on this page based on EMP, just to demonstrate getting the values from the ENAME column into an array.

On Demand process, called "demoProcess"

DECLARE
l_f01 VARCHAR2(200);
BEGIN
   FOR i IN 1..apex_application.g_f01.count
   LOOP
      l_f01 := l_f01 || apex_application.g_f01(i) ||',';
   END LOOP;
   l_f01 := rtrim(l_f01, ',');

   htp.p('P18_EMPNO: ' || :P18_EMPNO || 
         ' - X01: '    || apex_application.g_x01 || 
         ' - F01: '    || l_f01 );
END;

Javascript:

PLEASE NOTE: there are calls to "console.log" in the javascript code. These will write to the console. In Firebug this is found simply on the "console" tab. In IE however you might encounter javascript errors. If so, open up the "developer tools" with F12 and rerun the page. (I put developer tools in quotes because what passes for it in IE can hardly be called so. Don't dev in IE unless you really must.)
//To demonstrate using one of the fnn-arrays to get an array of data to the server.
//In this case all values in the ENAME column
var lArray = [];
$("td[headers='ENAME'] input:visible").each(function(){
   lArray.push($(this).val());
});
//---------------------------------------------------------------------
//htmldb_Get
//works in all versions, but has never been officially documented
var ajaxRequest = new htmldb_Get(null, 
                                 $v('pFlowId'), 
                                 'APPLICATION_PROCESS=demoProcess', 
                                 $v('pFlowStepId')
                                );
ajaxRequest.addParam('x01', 'Temporary Variable x01');
ajaxRequest.addParam('f01', lArray);
ajaxRequest.add('P18_EMPNO',$v('P18_EMPNO'));
//sync
//this is how this request is usually seen used
//a synchronous call will "lock" up the browser until the call has completed
var ajaxReturn = ajaxRequest.get();
console.log(ajaxReturn);

//async
//A method often overlooked, which will do the call asynchronous. 
//However, involves a bit more code and thus feels a bit more obscure than
// a jQuery alternative
ajaxRequest.GetAsync(function(pResponse){
   if(pResponse.readyState==4 && pResponse.status==200){
      console.log(pResponse.responseText);
   };
});
//---------------------------------------------------------------------
//jQuery post, async
//An alternative method which works where jQuery is included. 4.0 and up.
//By default this request is asynchronous, but if required can be made 
//asynchronous by adjusting the "async" param. See the docs!
//p_arg_names + values: for page items. Arrays!
var lArgNames = ['P18_EMPNO'],
    lArgVals  = [$v('P18_EMPNO')];

$.post('wwv_flow.show', 
       {"p_request"      : "APPLICATION_PROCESS=demoProcess",
        "p_flow_id"      : $v('pFlowId'),
        "p_flow_step_id" : $v('pFlowStepId'),
        "p_instance"     : $v('pInstance'),
        "x01"            : 'Temporary Variable x01',
        "f01"            : lArray,
        "p_arg_names"    : lArgNames,
        "p_arg_values"   : lArgVals
        },
        function(data){
           console.log(data);
        }
      );
//---------------------------------------------------------------------
//new apex.server namespace in 4.2, async
//This should be the preferred method starting from apex 4.2.
//It offers all the flexibility of jQuery, and it is well documented by the
//apex team. Here you do not have to specify parameters like p_flow_id, nor
//have to use p_arg_names/values. pageItems is a very useful addition aswell!
//Since you can freely change the parameters used for the jQuery call, you
//again can make the call synchronous if you would require so.
//Note that i provide the dataType parameter. apex.server.process will by
//default use "json", so if you just put out some text through your process
//you need to change this as otherwise you will encounter javascript errors.
apex.server.process('demoProcess',
                    {"pageItems":"P18_EMPNO",
                     "f01":lArray,
                     "x01":"Temporary Variable x01"
                    },
                    {"dataType":"text", 
                     "success":function(data){
                                  console.log(data);
                               }
                    }
                   );

11 comments:

  1. Hi, Tom. Your example was the first one that seemed to solve my problem, but something is not working. i have a proccess that uses apex_application.g_f01. When the proccess is defined as 'On Submit', it works fine. But if i define it as 'On Demand', apex_application.g_f01 is always empty. It seems that it gets filled at submit time. When I saw the first lines of your javascript code, i thought that was the answer. is that the purpose of that lArray?

    ReplyDelete
  2. the f01 array has to be filled up with values. In the javascript code lArray is passed in for f01. Right beneath where I declare this array there is a jQuery each loop over a set of input items, whose values will be pushed into the array. This works on submit without troubles because that all happens automatically because of the "name" attribute on those items, and apex will map it to that array. For ajax you need to do the manual step of putting the values into an array and providing it to the request in order to have those values server-side.
    Does that clarify it?

    ReplyDelete
  3. Hi Tom, I tried the apex.server.process example and filled the lArray properly:

    apex.server.process('Save f01 Array into the APEX session',{"f01":arr_f01},{"success":function(data){apex.debug('success');}});

    I call a Application Process which is a Ajax Callback function on the same site. I put debug outputs there to control the state of apex_application.g_f01(1);:

    da_debug('x: ' || apex_application.g_f01(1));

    It worked. BUT when I branch to a different page and do the exact same backend debug output it is empty, I have no idea why.
    Shouldn't it be stored for the session?

    ReplyDelete
  4. Sorry, got caught up in the session thing. apex_application.g_f01 has no APEX session, just a DB-session, and every processing / rendering has a new one, so the values are gone :-)

    ReplyDelete
    Replies
    1. That's correct, the f## arrays and x## items are only stored for one page view, so if you want to retain them you could use the apex collections. :-)

      Delete
  5. I have a long running process - if I submit the page to call it it timesout. I'm just changing this to simply call via js and provide no feedback on when it is complete....but would using an on demand process allow me to get around the timeout and provide feedback to the user via htp.p? Or will it also time out the same?

    ReplyDelete
  6. Hi, do you have a sample application whats you are trying to accomplish here? iam lost?

    ReplyDelete
  7. Hi ToM
    Calling an On Demand process with apex.server.process for fxx arrays greater than f20 fails with a signature error (f01 to f20 are OK). Not sure why there is this limitation since APEX_APPLICATION support G_F01 to G_F50.

    Jon

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete
  9. Tom,
    I have a question about Ajax calls via JavaScript on on an APEX 5 page.

    Using APEX 5 with Firefox ESR 38.2 and Oracle 11g.

    I have a small application in APEX 5 and I am trying to call an on-demand process via apex.server.process within a small JavaScript function on a page. However it seems that the code is NOT being executed and I am not seeing anything on my firebug console..



    What I have is a JavaScript function in the function and global declaration section of a page. This is the actual function:



    function myFunction(p_url,p_review_date,p_bsr_tracking_id) {



    if (!p_review_date)

    {

    var r = confirm("Upon clicking 'Ok' you agree that\nyou have read and reviewed the\nselected BSR package");



    if (r == true) {

    alert(p_bsr_tracking_id);



    // First update the date reviewed for the report, for compliance data tracking



    // HERE IS THE CALL TO DUMMY APPLICATION PROCESS



    apex.server.process ("DUMMY"

    ,{x01: p_bsr_tracking_id}

    ,{dataType:"text"}

    );

    alert("After the Ajax call!!") ;

    // Next open the new window showing the report to be viewed



    var a = window.open(p_url )

    }

    }



    else {

    alert("BSR Package has already been Reviewed");

    }



    }



    Here is a copy of the DUMMY application process:



    BEGIN



    logger.log(apex_application.g_x01);



    UPDATE Bsr_Tracking

    SET Date_Reviewed = SYSDATE

    WHERE Bsr_Tracking_Id = apex_application.g_x01;



    COMMIT;



    END;



    Do you see anything that would be causing this to NOT function?



    Thank you,



    Tony Miller

    Los Alamos, NM

    ReplyDelete
  10. Found issue, authorization scheme on on demand process was set to not allow public user to execute...

    Now just need to find a method in JavaScript function to refresh the calling interactive report..

    Thank you,

    Tony Miller
    Los Alamos, NM

    ReplyDelete