When there is a background processing where the user should wait until it's done, I don't want him/her to click anywhere else in the screen. But I also want the user to know s/he'll have to wait.

Overdemanding?

I found a very simple solution to this problem. Sure there is more elegant solutions without dojo, but let me share it...

This is the main screen:

Image:XPages Tip: A modal waiting dialog for background processes.

When user clicks the pointed button, there is a background process which would take 20-25 seconds. It is a long time for a web application. So we will somehow alert user that s/he has to wait a moment...

This is the initial code of the button:

<xp:button value="Puanla..." id="button1" styleClass="lotusBtn"> 
    <xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="termsPanel">
            <xp:this.action><![CDATA[#{javascript:scoreTerm(termEntry.getDocument())}]]></xp:this.action>
    </xp:eventHandler>
</xp:button>


First we activate dojo and add needed modules in our XPage/custom control:

Image:XPages Tip: A modal waiting dialog for background processes.


Image:XPages Tip: A modal waiting dialog for background processes.

Now we will create our dialog div. You can create it anywhere in the page:

<div id="progressDialog" dojoType="dijit.Dialog" title="Please wait..."> 
     <div>
             <xp:image url="/inProgress.gif" id="image3"></xp:image>
     </div>
</div>


We could use "xp:panel" tag here and set dojo attributes. It's a matter of preference. "inProgress.gif" is an animated image just to create an illusion of progress :)

Now we create a couple of client side javascript functions to show and hide this dialog. Here is a little trick. We don't want the dialog closable. Since I could not find a solution for 'non-closable' dialog, just googled and used this approach.

function scoringCompleted() { 
     dlg=dijit.byId("progressDialog");
     dlg.hide();
}

function scoringStarted() {
     dlg=dijit.byId("progressDialog");
     dlg._onKey = function(){return false;};
     dlg.show();
}


Here (in the bold part), we set a widget event to prevent "ESC" key. Escape keystroke stop loading of the page but does not close the dialog. If you are using a background agent for processing, escape will not stop the processing. Meanwhile, a little close icon is automatically placed on the title bar of the dialog. We should hide it as well. Put the following code to the CSS file.

#progressDialog .dijitDialogCloseIcon {display:none;}


Now, it comes to the crucial part. We want to show our dialog when background process started and hide it finally. We simply change the button code as follows:

<xp:button value="Puanla..." id="button1" styleClass="lotusBtn"> 
     <xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="termsPanel"
                     onStart="scoringStarted()"
                     onComplete="scoringCompleted()"
>
             <xp:this.action><![CDATA[#{javascript:scoreTerm(termEntry.getDocument())}]]></xp:this.action>
     </xp:eventHandler>
</xp:button>


Result is something like that:

Image:XPages Tip: A modal waiting dialog for background processes.

Remember, onStart and onComplete parameters will only work with partial refresh... You may also use onError for handling errors (like time out).

UPDATE!!!


Tim suggested far better solution to use dojox.widget.Standby. I updated start and complete functions and now there is no need to create divs or css on my page. It may be parameterized laterfor different purposes. Here are my functions:

function scoringStarted() { 
       if(this.fullStandby==null) {
               this.fullStandby = new dojox.widget.Standby({
                       target: document.forms[0] // it was dojo.body() which creates a problem in some dojo versions.
               });
               document.body.appendChild(fullStandby.domNode);
               fullStandby.startup();
       }
       fullStandby.show();
}

function scoringCompleted() {
       if(this.fullStandby!=null) fullStandby.hide();
}


Target object can be customized for a region in screen...
Serdar Basegmez   |   May 10 2011 07:42:00 AM   |    Development  Dojo  Tips  XPages    |  
  |   Next   |   Previous

Comments (10)

Gravatar Image
Daniel Friedrich    http://xpagesandme.wordpress.com    05/15/2014 11:24:21 AM

Great Post, thank you for sharing!!

Gravatar Image
Nachiket Bhandarkar       12/06/2013 6:29:00 AM

Fantastic!!! Thanks a ton for posting this.!!!

Gravatar Image
Ryan       05/12/2011 11:15:18 AM

Yeah, I'm stumped at the moment. If anyone comes across a solution please share! I'll respond if I find anything.

Gravatar Image
Serdar Basegmez    http://www.developi.com    05/11/2011 12:39:38 PM

I don't think there is a solution for this. Because the page is not being loaded partially in XPages (not like a CGI app).

Maybe you may use a intermediate page for this ?

But it's a good idea, worth looking for...

Gravatar Image
Ryan       05/11/2011 9:43:21 AM

@Serdar, I have an XPage that takes a few seconds to load and I want to display a loading prompt onload and oncomplete of the page.

@Tim, I imagine I can put something like this directly on the XPage to hide the dialog with XSP:addOnLoad:

<xp:scriptBlock id="scriptBlock1">

<xp:this.value><![CDATA

XSP.addOnLoad

(

function scoringCompleted()

{

dlg=dijit.byId("progressDialog");

dlg.hide();

}

scoringCompleted();

)

]></xp:this.value>

</xp:scriptBlock>

I'm having issues getting the dialog to show on page load. What event in XPages would be the best to use for this? Another scriptBlock?

Gravatar Image
Serdar Basegmez    http://www.developi.com    05/11/2011 3:01:30 AM

Tim,

It's fantastic and elegant :)

I will use it and update the post. Thank you!

Gravatar Image
Tim Tripcony    http://xmage.gbs.com    05/10/2011 1:32:08 PM

@Ryan, you can schedule any code to run when the page has finished loading by using the following pattern:

XSP.addOnLoad(function(){

// insert your code here

});

@Serdar, another dijit you may find interesting for this purpose is dojox.widget.Standby: it allows you to mask a specific portion of the page instead of modally blocking the entire page. For example, if you know a certain partial refresh event tends to be slow, you can mask just the partial refresh target for the duration of the event, while still allowing the user to interact with the remainder of the page.

Gravatar Image
Serdar Basegmez    http://www.developi.com    05/10/2011 9:45:33 AM

Ryan,

Can you specify an example?

Gravatar Image
Ryan       05/10/2011 9:35:42 AM

Does anyone know of a way to implement something like this on page load / page complete without having it attached to an onclick button event?

Gravatar Image
mark hguhes    http://hughesconnect.com    05/10/2011 8:42:43 AM

Another thing you can do is put a div above the image, and pass text to the function so you could do something like

scoringStarted('Loading')

scoringStarted('Saving Document')

scoringStarted('Updating Records...')

scoringStarted('Searching Records...')

and set the div content to the text to customize the message