function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
alexannicalexannic 

S-control page loads only after the whole script is finished!

Hello,
I have written some S-control scripts using AJAX toolkit, and some of them take some time to complete. It would be nice to show some text while parts of the scripts are running so that the user has some feedback on what's happening.
Currently, the S-control appears in a popup window and is blank until the whole script is finished. My code is as follows:
Code:
function initPage() { 
document.getElementById('divErrorMsg').innerHTML = "starting...";

//Get Salesforce connection based on users current session id 
sforceClient.registerInitCallback(setupPage);
sforceClient.init("{!API_Session_ID}", "{!API_Partner_Server_URL_70}", true);
then setupPage function executes...and finally the HTML appears:
Code:
<body onload="initPage();"> 
<center> 
<DIV id="divErrorMsg"></DIV> 
</center> 
</body> 
</html>
Is there any way I can update the div element during the execution of the script?
Many thanks.
 

 

salesmarchsalesmarch
If you find a way to insert a status message in the pop-up let me know. If we find one I'll post as well.
alexannicalexannic
It seems that I'm not the only one user with this problem.
Can anyone provide any assistance?

How can I refresh a div element in the page?

Thanks.
Ron HessRon Hess
The browser is running as fast as it can, and it won't paint it's screen unless it gets done with the code it is executing, this is a screen drawing optimization.
So if you want to "show a message" to provide a good user experience, you must allow the browser to "catch it's breath"

This can be done with setTimeout()

So in your example you would do something like this
function initPage() { 
document.getElementById('divErrorMsg').innerHTML = "starting...";
setTimeout("initPage2();", 50);
}

function initPage2() {
//Get Salesforce connection based on users current session id
blah, blah....

the 50 msec is enough ( usualy ) for the browser to paint the DIV innerHTML and then the second initPage2 gets called and real work begins.

you can play with the timout to match your needs.

alexannicalexannic
Dear Ron,
Thank you for your answer.
I have tried the setTimeout() function but with no luck. My S-Control will popup an HTML form with 15 textboxes, allowing the user to enter many product codes and quantities into an Opportunity at once. This is my code:
Code:
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
    <head>
    <title>Μαζική εισαγωγή κωδικών</title>
    <head>
        <script language="javascript" src="https://www.salesforce.com/services/lib/ajax/beta3.3/sforceclient.js" type="text/javascript"></script>
        <script>
           <!--
            function initPage() {
             
                sforceClient.registerInitCallback(setup);
                sforceClient.init("{!API_Session_ID}", "{!API_Partner_Server_URL_70}", true);

           }
            //Use this function as the entry point for your DHTML and JAVASCRIPT processing
            function setup() {
             document.getElementById('divProgress').innerHTML = "Παρακαλώ εισάγετε κωδικό - ποσότητα:";

            }

function ExecuteSave() {
document.getElementById('divProgress').innerHTML = "executing...";
var OppLinesArray = new Array(15);
for (var x = 1; x < 16 ; x++) {
var CodeText = document.codeform.elements["code"+x];
if (IsEmpty(CodeText) == false) {
var CurCode = document.codeform.elements["code"+x].value;
var CurQuant = document.codeform.elements["quant"+x].value;
var PriceBookId = GetPriceBookId(CurCode);
document.getElementById('divProgress').innerHTML = CurCode;
//alert("asd");
var queryResult = sforceClient.query("Select Id from Product2 WHERE ProductCode = '"+CurCode+"'", GetPriceBookId);


//var OppLine = new Sforce.Dynabean("OpportunityLineItem");
//OppLine.set("Opportunity__c","{!Opportunity_ID}" );
//OppLine.set("Product2__c", CurProdId);
}


}

//OppLinesArray[x] = OppLine;

return false;
}

function GetPriceBookId(ProdQueryResult){
//prwta to ProductId2
//var ProdQueryResult = sforceClient.Query("Select Id from Product2 WHERE ProductCode = '"+code+"'");

if (ProdQueryResult.size == 1) {
var Prod = ProdQueryResult.records[0];
var Product2ID = Prod.get("Id");
}


var ProdQueryResult2 = sforceClient.Query("Select Id from PricebookEntry WHERE Product2ID = '"+Product2ID+"'");

if (ProdQueryResult2.size == 1) {
var Prod2 = ProdQueryResult2.records[0];
var PriceBookId = Prod2.get("Id");
}
return PriceBookId;
}
function IsEmpty(aTextField) {
if ((aTextField.value.length==0) ||(aTextField.value==null)) {
return true;
}
else { return false; }
}

//-->
</script>
<style type="text/css">
td.status { font: 900 14px arial ; background: #747E96; color: #FFFFFF }
</style>
</head>
<body onload="initPage()">
<div id="output"></div>
<table>
<tr>
<td class="status" id="divProgress" /> </tr>
</table>
<form name="codeform" method="post" action="">
<input type="text" name="code1" size="15"> <input type="text" name="quant1" size="6"><br>
<input type="text" name="code2" size="15"> <input type="text" name="quant2" size="6"><br>
<input type="text" name="code3" size="15"> <input type="text" name="quant3" size="6"><br>
<input type="text" name="code4" size="15"> <input type="text" name="quant4" size="6"><br>
<input type="text" name="code5" size="15"> <input type="text" name="quant5" size="6"><br>
<input type="text" name="code6" size="15"> <input type="text" name="quant6" size="6"><br>
<input type="text" name="code7" size="15"> <input type="text" name="quant7" size="6"><br>
<input type="text" name="code8" size="15"> <input type="text" name="quant8" size="6"><br>
<input type="text" name="code9" size="15"> <input type="text" name="quant9" size="6"><br>
<input type="text" name="code10" size="15"> <input type="text" name="quant10" size="6"><br>
<input type="text" name="code11" size="15"> <input type="text" name="quant11" size="6"><br>
<input type="text" name="code12" size="15"> <input type="text" name="quant12" size="6"><br>
<input type="text" name="code13" size="15"> <input type="text" name="quant13" size="6"><br>
<input type="text" name="code14" size="15"> <input type="text" name="quant14" size="6"><br>
<input type="text" name="code15" size="15"> <input type="text" name="quant15" size="6"><br>
<input name="Submit" type="submit" id="Submit" value="Καταχώρηση" onclick='return ExecuteSave()'>


</form>
</body>
</html>

 

At the press of Submit button, the ExecuteSave function is called. For every code found in the textboxes, its Product2ID is retrieved with the GetPriceBookID function. I have a line (in bold) where I update the divProgress element with the current product code.
I suppose that since I'm using AJAX the screen will be refreshed! What happens is that the button freezes when pressed and after a while when the script is finished I see only the last code.

Isn't AJAX supposed to allow asynchronous calls?
Shouldn' then the screen be updated every time the innerHTML function is called?

Many thanks for your time.
Alex
NPower_EvanNPower_Evan
I struggled with this for a while too.  Most of the Ajax functions have the option to call them asynchronously, although it isn't documented very well and none of the samples use it.  You have to provide a callback function for each query or retrieve call, and set the async flag to true.  The result is always passed in with the callback.

Here's the doc (for some reason, the function prototype doesn't have the async flag, but the parameters list does):

query

QueryResult/SoapFault query(<String> soql, <Function> callback)
    Performs a query operation
      Parameters:
        soql - The sforce object query language that constitutes a selection.
          callback - A pointer to a function to be called once the http request has returned.
            async - A flag to indicate that the call should be made asynchronously or not. A value of true will cause the method to return immediately and once a response is recieved, the callback function will be invoked and passed the results of the call.
            And here's some code I'm using.  It calls asynchronously, and the message appears while you wait:

            function CreateAccountAndContact() {

                // display a message next to the button
                var msg = document.getElementById("message");
                msg.innerHTML = "Creating account. DO NOT CLICK AGAIN.";

                // create a new account
                var acc = new Sforce.Dynabean("Account");
                acc.set("Name",accountname);
                acc.set("BillingStreet", document.getElementById ('00N30000001Hfh4').value);
                acc.set("BillingCity", document.getElementById ('00N30000001Hfgb').value);
                acc.set("BillingState", document.getElementById ('00N30000001Hffw').value);
                acc.set("BillingCountry", document.getElementById ('00N30000001Hffs').value);
                acc.set("BillingPostalCode", document.getElementById ('00N30000001Hfgl').value);
                acc.set("RecordTypeId", '01230000000DJPAAA4');
                sforceClient.Create([acc], step2, true);
            }

            var accid="";

            function step2(saveResult) {
                // create the contact
                accid=saveResult[0].id;
                var contact = new Sforce.Dynabean("Contact");
                contact.set("AccountId", accid );
                contact.set("FirstName", con1fname);
                contact.set("LastName", con1lname);
                contact.set("Phone", document.getElementById('c1phone').value);
                contact.set("HomePhone", homephone);
                sforceClient.Create([contact], step3, true);
            }

            function step3(saveResult) {
                // open the new account record
                parent.frames.location.replace("/"+accid);
            }


            My code actually has many more "steps" -- each in another function.  Each toolkit call sets up the callback to the next.  It gets tricky, though, because your local variables disappear between calls.  If you want to do a bunch of steps with the same account, make sure your variables (like accid above) are declared with global scope (outside any function) so they will be available to all the functions.

            Crazy stuff.  My s-controls are working a lot better this way, and they appear to be much more responsive.

            Evan Callahan
            NPower Seattle

            alexannicalexannic
            Many thanks for your reply Evan!
            Your code shows exactly the effect I want to achieve however it has the following limitation:

            I have a form where the user enters 15 product codes that are to be entered in an Opportunity.
            My code loops through these 15 product codes and each time searches for their product2ID value.
            At the end, all those ProductIDs are entered in OpportunityLine objects and saved.

            In other words...
            Code:
            for (var x = 1; x <= (NoProductsToAdd) ; x++) {
            sforceClient.query("Select Id from Product2 WHERE ProductCode = '"+EnteredCode+"'",GetProductID,true);
            }
            
            function GetProductID(queryResults) {
            <process results here>
            }

            The problem:
            The loop ends before the asynchronous calls return the results, and the save function attempts to save an empty dynabean.

            The code works if the process can be split into distinct steps, but what can you do in a loop situation? You certainly can't write 15 identical steps, one for each code??

            Once again I appreciate the help!
            Alex

            Message Edited by alexannic on 10-14-2006 10:34 AM

            NPower_EvanNPower_Evan
            Two options:  First, I think your query could be done all at once with a call to the Retrieve function.  This would be a lot faster (and works asynchronously like Query):

            var PCodes = new Array();
            //put the codes into this array here

            sforceClient.Retrieve("Id", "Product2", PCodes, GetProductIDs, true);

            function GetProductIDs(arrayOfIDsFromProduct2) {
                // process all resulting codes here
            }


            Alternatively, you can query asychronously in a loop.  Your first call sets up the loop, then your result function gets called for the first record and in turn calls itself for the next record:

            var ProductsToAdd = 0;  // must be declared outside any function

            if (ProductsToAdd>0) {
            sforceClient.query("Select Id from Product2 WHERE ProductCode = '"+EnteredCode+"'",GetProductID,true);
            }

            function processOneProduct(queryResults) {
            if ((ProductsToAdd > 0) && (queryResults.size > 0)) {

            // process this one...

            // display status (from your code)
            document.getElementById('divProgress').innerHTML = CurCode;


            // then get the next one
            ProductsToAdd -= 1
            sforceClient.query("Select Id from Product2 WHERE ProductCode = '"+EnteredCode+"'", processOneProduct ,true);

            } else {

            // you are done, move on....

            }
            }