Skip to main content

My question is along the same lines as this post How to show wait message that is used by actions.

I am trying to accomplish the same using the Action Framework.  See screenshot below:


7d11e836aff1c4fe1fc6351fa5082888c8eac5e6.png

My issue is that the first Show Message and block UI is never shown (or is happening so fast that it is not perceptible).  The async remote call is executed and falls through to the next action w/o waiting until the promise is invoked.

What are my options?  Any guidance is appreciated.

Best,
Irvin

Irvin, what does your Snippet look like? What you’ll probably need to do is to re-write your Snippet to make it return a jQuery Deferred’s Promise, which you can resolve in the onSuccess of your call to sforce.connection.remoteFunction(), for instance. By having your Snippet return a jQuery Promise, the Action Framework knows that you are doing an asynchronous operation, and waits until the Promise is resolved to carry on with additional steps.

So for instance, you could rewrite your Snippet to look something like this:


// Create a jQuery Deferred<br>var dfd = $.Deferred(); <br>var url = '/some/remotesite';<br>var sessionId = skuid.utils.userInfo.sessionId; <br>sforce.connection.remoteFunction({<br>&nbsp; &nbsp;url : url,&nbsp;<br>&nbsp; &nbsp;requestHeaders: {<br>&nbsp; &nbsp; &nbsp; "Authorization":"Bearer "+sessionId,&nbsp;<br>&nbsp; &nbsp; &nbsp; "Content-Type":"application/json",&nbsp;<br>&nbsp; &nbsp; &nbsp; "Connection":"Keep-Alive"&nbsp;<br>&nbsp; &nbsp;},&nbsp;<br>&nbsp; &nbsp;method: "GET",&nbsp;<br>&nbsp; &nbsp;onSuccess : function(response) {&nbsp;<br>&nbsp; &nbsp; &nbsp; dfd.resolve(response);&nbsp;<br>&nbsp; &nbsp;},&nbsp;<br>&nbsp; &nbsp;onFailure : function(response) {&nbsp;<br>&nbsp; &nbsp; &nbsp; dfd.reject(response);<br>&nbsp; &nbsp;}&nbsp;<br>}); <br>// Return the Deferred's Promise<br>return dfd.promise();



Notice how we create a Deferred, then in the onSuccess function we call dfd.resolve() to indicate that our asynchronous operation completed successfully, and in the onFailure function we call dfd.reject() to indicate that something went wrong. The Snippet then returns the Deferred’s Promise, telling Skuid to delay further Actions until the Deferred is either resolved — in which case the Action sequence continues — or rejected, in which case any On-Error Actions defined for this action will be executed.


Hi Zach,

Simply returning the promise did the trick.  

Is this documented anywhere?  If not, this would be a great FAQ.  

As always, thanks for the guidance and timely reply.

Best,
Irvin


Not currently documented anywhere, we definitely need to add an article about this.


Agreed! This post is super-helpful. It would be great to have some documentation on this.


I am having a similar problem.

Can this Deferred Promise technique be used with sforce.apex.execute?

Or do I have to rewrite my code to use sforce.connection.remoteFunction?

Here’s my code snippet:


var params = argumentst0], $ = skuid.$; $.blockUI({ message: 'Waiting...', timeout: 5000 }); var MtgModel = skuid.model.getModel(Items); var eventModel = skuid.model.getModel('Event'); var eventRow = eventModel.getFirstRow() var mtgIds = ''; $.each(MtgModel.getRows(),function(i,row){ if (myIds !== '') myIds = myIds + ','; myIds = myIds + row.Id; }); var result = sforce.apex.execute( 'MyWebservice','MyFunction',{inputString:myIds ,eventId:eventRow.Id} ); $.unblockUI(); $.blockUI({ message: 'Done<br>' + result, timeout: 5000 }); 

Suggestions on how to adapt this to properly block the UI are appreciated.

Thanks,
Tony


Tony,

From what I’ve seen, sforce.apex.execute() is synchronous, meaning that JavaScript’s execution should be blocked until your call finishes, so I would expect that the sequence here would work fine, although you don’t need the $.unblockUI() call at the end — the subsequent $.blockUI() call with a timeout will unblock the UI after 5000 milliseconds. If you’re seeing the UI get unblocked before sforce.apex.execute finishes, then that would be surprising.


Thanks Zach.

I tried removing the unblockUI call. Still a problem.

The behavior I am seeing is this:

– First Block UI message never shows
– A couple seconds go by as the apex code runs
– the Second Block UI message shows once the apex is done

Can’t think of any reason why the sforce.apex.execute would unblock the UI but that seems to be what is happening.

Thoughts?

Tony




Can you put some console.logs in place so we can get an idea of what’s happening? Try this:

var params = argumentst0],
$ = skuid.$;
console.log(‘before show waiting message’);
$.blockUI({ message: ‘Waiting…’, timeout: 5000 });
console.log(‘after show waiting message’); 
var MtgModel = skuid.model.getModel(Items);
 var eventModel = skuid.model.getModel(‘Event’);
 var eventRow = eventModel.getFirstRow();
 var mtgIds = ‘’;
$.each(MtgModel.getRows(),function(i,row){
    if (myIds !== ‘’) myIds = myIds + ‘,’;
    myIds = myIds + row.Id;
 });
console.log(‘before execute apex’); 
var result = sforce.apex.execute( ‘MyWebservice’,‘MyFunction’,{inputString:myIds ,eventId:eventRow.Id} );
console.log(‘after execute apex’); 
console.log(‘before block ui with done message’); 
$.blockUI({
 message: 'Done ’ + result,
 timeout: 5000
});
console.log(‘after block ui with done message’); 


Sure thing. Here’s what I got from my Firefox Console

before show waiting message Test:333:1
after show waiting message Test:339:1
before execute apex Test:355:1
POST XHR https://c.cs11.visual.force.com/services/Soap/package/MyWebservice >HTTP/1.1 200 OK 6747ms]
after execute apex Test:359:5
before block ui with done message Test:360:5
after block ui with done message Test:367:1
POST XHR https://c.cs11.visual.force.com/apexremote


Note that it waits for 6.7 seconds while it makes the Apex call.





Do the messages all show up at once, or does the “after execute” message show up only once the Apex call finishes?


There is a long delay while the apex executes before the “after execute apex” message appears.


Hey Zach - Any thoughts on this?


Would still love to resolve this issue.  What’s my next step here?


Tony, are you running this Snippet as part of an Action Framework sequence? If so, can you post a screenshot of your action sequence?


It’s a pretty simple Action Framework



I tried blocking and unblocking the UI in the action framework rather than in the snippet but that also did not work properly:



In this case the first Show message does not appear until after the Snippet has run. Then the first message appears, then when the second message appears immediately after.


If I comment out the “sforce.apex.execute” call it works as you would expect with the first message showing immediately upon clicking the navigation item.


I could send you a video or give you access to my org if you would like.


Can you try removing the Query 2 Models action and see if that makes the initial Block UI take effect?


When I remove the Query 2 Models it goes:

1. click
2. wait a second or so while snippet runs
3. second block UI message appears

First block UI message never appears.


Wow, this is quite confusing. My recommendation at this point would be to follow the typical debugging path of removing code until you get it to work, then code back in until it stops. So I’d start by just having the code to run your first blockUI message, comment out everything after that. Once you (hopefully) get this to show as expected, start adding in additional code till the first blockUI message stops showing, then we’ll at least have a better idea of which piece of code is the culprit, or if it’s even related to this code at all.


I finally got it to work. The secret was putting my remote call in the “onBlock” parameter to the blockUI call. That way the remote call does not happen until after the onBlock has truly blocked the UI. I have no idea why this is necessary, but it seems to work.

This was key: http://malsup.com/jquery/block/#options


var params = argumentse0], $ = skuid&#46;$; $&#46;blockUI({ message: 'Before Remote Message', onBlock:function(){ &#47;&#47;do stuff var result = sforce&#46;apex&#46;execute( 'MyWebservice','MyFunction',{inputString:ids} ); skuid&#46;model&#46;updateData(aModel1,Model2,Model3],function(){ $&#46;blockUI({ message: 'After Remote Message' + result, timeout: 2000 }); }); } }); 

I’m having trouble getting this snippet to work with the deferred promise. I’ve got the same thing set up on other snippets working fine. What’s wrong here?


var params = argumentsu0], $ = skuid.$; var dfd = $.Deferred(); // define and save model to search through for criteria var criteriaModel = skuid.model.getModel('Collateral'); criteriaModel.save({callback: function(result){ // if model saves, proceed if (result.totalsuccess) { console.log(criteriaModel+'save successful'); var rows = criteriaModel.getRows(); // set the model we are querying and define all conditions var templates = skuid.model.getModel('ConditionTemplateSearch'); var condition = templates.getConditionByName('CollateralCategories__c'); var leased = templates.getConditionByName('CollateralLeased__c'); var owned = templates.getConditionByName('CollateralOwned__c'); // set the destination model to push rows into var allTemplates = skuid.model.getModel('ConditionTemplates'); // for each selected row of the criteria model $.each(rows, function(){ var currentId = this.Id; // set conditions on the template search model var row = this; var isLeased = criteriaModel.getFieldValue(this, 'Leased__c'); console.log(isLeased); if (isLeased) { templates.activateCondition(leased); templates.deactivateCondition(owned); } else if (isLeased === false) { templates.activateCondition(owned); templates.deactivateCondition(leased); } var category = criteriaModel.getFieldValue(row, 'Category__c'); templates.setCondition(condition,category); // query the model and add any rows found to the allTemplates model templates.updateData( function(){ var newRows = templates.getRows(); allTemplates.adoptRows (newRows); } ); }); dfd.resolve(); // if save fails, do nothing } else { console.log(result.insertResults); dfd.reject(); } } }); return dfd.promise();<br>

I’m trying to run several snippets in a row, synchronously, and in this snippet the dfd is never recognized, the ui is never blocked, even if it Just have in my action framework, show message and block ui, this snippet, and unblock ui. 


I find the jquery $.when() more reliable than the .save(callback) method.

Have you tried
$.when(model.save())
    .done(function(){
       //do your work here
       dfd.resolve();
     })
     .fail(funcion(){
       dfd.reject();
     });

return dfd.promise();


Also, you might want to try
var dfd = new $.Deferred();


adding new worked, and then it didn’t. When it worked, it delayed the running of the second snippet until the first resolved, but never showed the block ui message. 

then it just stopped working. I’ve stopped trying to save, just trying to get the snippet to trigger a pause in the action framework until complete. can you help??

This is not pausing the sequence:


var $ = skuid.$; var dfd = new $.Deferred(); // define and save model to search through for criteria var criteriaModel = skuid.model.getModel('Property'); var rows = criteriaModel.getRows(); // set the model we are querying and define all conditions var templates = skuid.model.getModel('ConditionTemplateSearch'); //var condition = templates.getConditionByName('CollateralCategories__c'); var leased = templates.getConditionByName('RealPropertyLeased__c'); var owned = templates.getConditionByName('RealProperty__c'); // set the destination model to push rows into var allTemplates = skuid.model.getModel('ConditionTemplates'); // for each selected row of the criteria model $.each(rows, function(){ var currentId = this.Id; // set conditions on the template search model var row = this; var isLeased = criteriaModel.getFieldValue(this, 'Leased__c'); console.log(isLeased); if (isLeased) { templates.activateCondition(leased); templates.deactivateCondition(owned); } else if (isLeased === false) { templates.activateCondition(owned); templates.deactivateCondition(leased); } // var category = criteriaModel.getFieldValue(row, 'Category__c'); // templates.setCondition(condition,category); // query the model and add any rows found to the allTemplates model templates.updateData( function(){ var newRows = templates.getRows(); allTemplates.adoptRows (newRows); } ); }); dfd.resolve(); return dfd.promise();<br>