Today there was a thread on cf-talk about how to make a "Loading" or "Please Wait" style page while ColdFusion is doing something slow. Most of the answers talked about AJAX but I thought I'd show a simpler version that just used a bit of JavaScript.
First I'll create my loading message:
1 <p id="loading">
2 Please stand by while we do something.
3 <cfoutput>#repeatString(" ", 250)#</cfoutput>
4 </p>
Note that I gave an ID to my loading block. This will be used later. Also note the repeatString. Why do I have that? One of the "features" of IE is that it will not render any content until it gets "enough" content. I use this block of spaces simply to slap IE around and force it to render the content. My next line of code is a simple CFFLUSH:
2 Please stand by while we do something.
3 <cfoutput>#repeatString(" ", 250)#</cfoutput>
4 </p>
1 <cfflush>
This is what tells ColdFusion to send the current output back to the browser. Now for the slow code. Obviously this will be custom for your application, but for my test I just used some Java:
1 <!--- slow process --->
2 <cfscript>
3 go_to = createObject("java", "java.lang.Thread");
4 go_to.sleep(3000); //sleep time in milliseconds
5 </cfscript>
You can find this code on the ColdFusion Cookbook entry.
Now I just need to clean up the loading text. I used this simple JavaScript:
2 <cfscript>
3 go_to = createObject("java", "java.lang.Thread");
4 go_to.sleep(3000); //sleep time in milliseconds
5 </cfscript>
1 <script language="javaScript">
2 loadingBlock = document.getElementById('loading');
3 loadingBlock.style.display='none';
4 </script>
And then I wrapped with a message to the user:
2 loadingBlock = document.getElementById('loading');
3 loadingBlock.style.display='none';
4 </script>
1 <p>
2 Thanks for waiting. Here is your important information.
3 </p>
2 Thanks for waiting. Here is your important information.
3 </p>


Comment 1 written by Jay McConathy on 29 November 2006, at 8:37 AM
Jay
Comment 2 written by Raymond Camden on 29 November 2006, at 8:43 AM
(Do you hear thunder?)
Comment 3 written by Jay McConathy on 29 November 2006, at 9:19 AM
I do not usually have the problem but I like the idea of a loading page sometimes. we pull huge quantities of data from databases and that can be slow at times so I appreciate the code.
Jay
Comment 4 written by Mike McConnell on 29 November 2006, at 9:21 AM
M. McConnell
Comment 5 written by Raymond Camden on 29 November 2006, at 9:26 AM
Comment 6 written by Jay McConathy on 29 November 2006, at 9:38 AM
hour glass and all??
Jay
Comment 7 written by Scott P on 29 November 2006, at 9:51 AM
<script language="javaScript">
document.getElementById('loading').style.display='none';
</script>
@jay
You can get some nice animated waiting style images from
http://www.ajaxload.info
Using one of those, your page could be this:
<span id="loading">
<img src="ajax-loader.gif" alt="please wait">
</span>
<cfflush>
page stuff here
<script language="javaScript">
document.getElementById('loading').style.display='none';
</script>
Comment 8 written by TJ Downes on 29 November 2006, at 10:32 AM
Comment 9 written by Joe Zack on 29 November 2006, at 10:47 AM
Que serrar!
Comment 10 written by Marcos Placona on 29 November 2006, at 10:48 AM
Comment 11 written by Emmet on 29 November 2006, at 11:42 AM
Comment 12 written by Jay McConathy on 29 November 2006, at 11:42 AM
Jay
Comment 13 written by Justice on 29 November 2006, at 12:06 PM
Comment 14 written by Tuyen on 29 November 2006, at 12:12 PM
<head>
<script language="JavaScript" src="jscripts/xp_progress.js"></script>
</head>
<p id="loading">Page Loading - Please Wait...</p>
<script type="text/javascript">
var bar1= createBar (300,15,'white',1,'black','blue',85,7,3,"");
</script>
<cflush>
<script type="text/javascript">
setTimeout("document.getElementById('loading').style.display = 'none'", 2000);
setTimeout("bar1.hideBar()", 2000);
</script>
You can find more about xp_progress.js here:
http://www.dynamicdrive.com/dynamicindex11/xpprogr...
Comment 15 written by Jay McConathy on 29 November 2006, at 1:10 PM
Comment 16 written by Brian Swartzfager on 29 November 2006, at 1:13 PM
I long for the day when all of my family members have broadband. :)
Comment 17 written by Christopher Wigginton on 29 November 2006, at 2:01 PM
You could also use the onload event, which would kick in once the entire page was loaded.
<script language="javascript">
window.onload = function(){
document.getElementById('loading').style.display='none';
}
</script>
Comment 18 written by Jay McConathy on 29 November 2006, at 2:45 PM
What has been posted here has been great though.
Comment 19 written by Raymond Camden on 29 November 2006, at 2:51 PM
Believe it or not I did this myself as well... in Perl... in 93 or so. ;)
Comment 20 written by Jay McConathy on 29 November 2006, at 3:01 PM
Comment 21 written by Brian Swartzfager on 29 November 2006, at 3:19 PM
Would using two GetTickCount functions to get the time it took to process the page do the trick? You could do it like this:
1. Put the first GetTickCount statement right after the <cfflush> and assign its value to a variable (say startTime).
2. Put the second GetTickCount statement at the end of your code and assign its value to a variable (endTime).
3. Subtract startTime from endTime and divide by 1000 to get the number of seconds it took to process the page.
4. Put a JavaScript block at the end of the code that writes that result to a <span> or <div> element at the top of the page (like “Load time: 83 seconds”).
Comment 22 written by Raymond Camden on 29 November 2006, at 3:23 PM
Comment 23 written by Jay McConathy on 29 November 2006, at 3:32 PM
Comment 24 written by Gary Fenton on 29 November 2006, at 5:17 PM
Taking an easy example, if you have 4 queries to perform just CFLUSH some javascript after each CFQUERY to knock a visual counter on the page up by 25%. It will only be accurate if all queries take the same amount of time to process, so in reality the progress bar will show the percentage of CFML processed rather than a percentage of time to wait. If you only have 1 big query to do then obviously this idea won't help you.
Comment 25 written by Tero Pikala on 30 November 2006, at 5:47 AM
Comment 26 written by Jay McConathy on 30 November 2006, at 8:02 AM
or run a time and display it upon completion.
I have still not gotten the hang of making javascript show time as it loads so if anyone has a simple example it would be great.
Comment 27 written by Tom McNeer on 30 November 2006, at 4:32 PM
I'd like to inject a twist into this whole issue, not specifically related to displaying loading messages, but rather to the underlying issue of long-running processes, especially as applied to Model-Glue. I've tossed this out on the MG list a couple of times, but without much response.
So -- you have a Model-Glue event that sets off a long-running process. One that runs long enough that it could possibly run into the default CF timeout (and you don't want to reset the default timeout on the installation just for this one process).
Where in the sequence of MG actions would you/could you place a CFSETTING timeout value that would prevent the process from timing out? And how would you set up an event structure that would allow for a "loading" message of some sort, as well as for some sort of check to see if the process were complete. Then, of course, the results would need to be displayed.
I'm well aware that I just massively overcomplicated this whole blog entry. But since you've been working with Model-Glue a lot lately, I'm hoping you (or someone else listening) might be able to provide a little guidance.
Comment 28 written by Raymond Camden on 30 November 2006, at 5:18 PM
http://cfconcurrency.riaforge.org/
I don't think you could do the "please wait" as we did though. You could tell the view to reload using JS, after like 3 seconds, and have the controllers check and see if the concurrent event is done yet.
Comment 29 written by Tom McNeer on 30 November 2006, at 5:37 PM
Thanks. But unfortunately, the site in question is running CF Standard, so the Asynchronous Gateway isn't available.
How about something like this (not my idea, suggested by someone smarter):
The originating event simply contains two result statements in its event-handler tag. One kicks off the long-running process. The second simply returns a view, which could contain a loading message.
The first event goes on its merry way -- can you put a CFSETTING tag in a CFC method? -- and when it's complete, sets a session variable.
In the view produced from the second event, there's an iframe or a refresh, or an AJAX call, or something, which fires an event which checks the status of that session variable. If the process is complete, a result tag fires a final event to show the results.
This sounds pretty close. But I think I'm missing something. And of course, I've gone way far afield from the original subject.
Comment 30 written by Raymond Camden on 1 December 2006, at 9:21 AM
Comment 31 written by Tom McNeer on 1 December 2006, at 10:06 AM
As far as the CFSCHEDULE suggestion goes: do you mean, use a CFSCHEDULE tag, scheduled to Now(), to call one of the events?
And -- one of the things I'm most confused about: where would I place a CFSETTING tag to avoid the long process from timing out? Can I simply place it in the method of the controller that's fired for the process, or perhaps in a global OnRequestStart method, applying the CFSETTING based on the name of the event?
I bring up the latter idea because in Mach-II, Peter Farrell has written a filter that can be applied to long-running events. If you apply the filter to an event and specify a timeout param, the CFSETTING appears to work throughout the event - or I guess, throughout the request. So I'm wondering if you could do much the same thing in MG,using an OnRequestStart method? Does any of that make sense?
Comment 32 written by Raymond Camden on 1 December 2006, at 10:30 AM
Yes to your cfschedule thing.
CFSETTING: I'd place it in Application.cfm and make it global. You could also do it in the controller for the event in question.
Comment 33 written by Tom McNeer on 1 December 2006, at 11:05 AM
Thanks for all your help.
As far as the sequence thing goes, what I was really asking (and I'll experiment to see the results on my own) was whether you could a) fire an event that simply announced two result; then b)the first resulting event would deliver some sort of "loading" view; and c) the second resulting event would actually call the long-running process. In that case, would MG render the loading view, then go on and start the process? If so, then it might be possible to check the progress of the event and deal with it.
Anyway, I'll play with it and see what happens. If I come up with anything useful, I'll share it.
Thanks again for your generous assistance.
Comment 34 written by David on 8 June 2010, at 3:45 PM
Comment 35 written by Raymond Camden on 8 June 2010, at 4:10 PM
Comment 36 written by Stephen on 21 July 2010, at 9:22 AM
I put this:
<span id="loading">
<img src="ajax-loader.gif" alt="please wait">
</span> <cfflush>
above all my long running queries to make it work. This puts it above my <html> tags. It seems to work just fine, but is this a bad idea?
Saw you at CFUnited last year. I hate that is is the last one. I can't come!
Comment 37 written by Raymond Camden on 22 July 2010, at 8:30 AM
Comment 38 written by Stephen on 22 July 2010, at 8:37 AM
Comment 39 written by Raymond Camden on 22 July 2010, at 8:39 AM
[Add Comment] [Subscribe to Comments]