I haven't played much with ColdFusion 9's new Ajax UI tags, mainly because I'm kind of a jQuery UI fan boy now, but this morning I took a look at cfProgressBar. It's a nice little utility and has some interesting uses. Here are a few examples to help you learn the tag. (And hopefully avoid some of the documentation issues I ran into!)
First, let's look at an incredibly simple progress bar - one that has absolutely no tie to a real process.
<html>
<head>
<script>
function init() {
ColdFusion.ProgressBar.start("mycfb")
}
</script>
</head>
<body>
<h2>Processing your stuff...</h2>
<cfprogressbar name="mycfb" duration="5000" interval="300" width="300" />
<cfset ajaxOnLoad("init")>
</body>
</html>
Right off the bat, let me point some documentation bugs that may snare you. First, the cfprogressbar tag must have an end tag. That's kind of silly and I'm sure it will be corrected for the final release.
Secondly, both the width and interval attributes are marked as optional with defaults. As far as I can see, this is not the case. Width for me defaulted to 100%, and when I didn't supply an interval, the progress bar never did anything.
Moving on - the last thing to note is that a progress bar will not start running until you tell it to. In this case, I have a 'static' progress bar that uses a duration attribute. This progress bar will simply update it's status of 5000 ms, or 5 seconds. I start the progress bar using ColdFusion.ProgrsssBar.start(name), and, well, that's it.
You can view a demo of this here: http://www.raymondcamden.com/demos/cfprogressbar/test.cfm
A static progress bar may not make much sense, but, I do think folks might have a use for it. You may have a slow process that has no direct API to check it's status. If you can roughly estimate how long the process normally takes, then this type of progress bar could at least give your user a reasonable idea of its progress. That being said, I think most folks will want to tie a progress bar to a real status. Let's look at an example of that now.
<cfset session.cfpbtest = 0>
<html>
<head>
<script>
function init() {
ColdFusion.ProgressBar.start("mycfb")
}
</script>
</head>
<body>
<h2>Processing your stuff...</h2>
<cfprogressbar name="mycfb" bind="cfc:process.getStatus()" interval="600" width="300" />
<cfset ajaxOnLoad("init")>
</body>
</html>
This example is much like the other, but note I've removed the duration and replaced it with a bind to a CFC. (Binds to JavaScript functions are also allowed.) My template initializes a session variable, cfpbtest, to 0. This is then used within my CFC:
component {
remote struct function getStatus() {
var result = {};
if(not structKeyExists(session, "cfpbtest")) session.cfpbtest = 0;
session.cfpbtest+=0.1;
if(session.cfpbtest > 1) session.cfpbtest = 1;
result.status = session.cfpbtest;
if(result.status == 1) result.message = "Woot! Done.";
if(result.status < 0.8) result.message = "Getting closer...";
if(result.status < 0.6) result.message = "More than halfway done...";
if(result.status < 0.4) result.message = "Still working, ways to go...";
if(result.status < 0.2) result.message = "Just begun...";
return result;
}
}
The API for progress bar bindings is pretty simple. The CFC method (or JavaScript function) does not take an attribute. It must return a structure with two keys, status and message. Status must be a number between 0 and 1. Message can be whatever you want, or blank even. WARNING: If you return a number higher than 1, the progress bar will correctly display it (i.e., it doesn't draw the progress past the end), but it will continue to peg your server. Notice my little check there to see if the value is over 1? I did that because I noticed floating point errors and an infinite loop of network calls. I've already logged a bug for this. Oh, and by the way, notice the interval value? If you set that too low, and you have an error in your CFC, you will get an infinite number of JavaScript alerts. I filed a bug report on that as well.
You can view this demo here: http://www.raymondcamden.com/demos/cfprogressbar/test2.cfm
Here is another example that demonstrates the onComplete functionality. In this example, I've specified that the progress bar should hide itself when done:
<cfset session.cfpbtest = 0>
<html>
<head>
<script>
function init() {
ColdFusion.ProgressBar.start("mycfb")
}
function hideme() {
ColdFusion.ProgressBar.hide("mycfb")
}
</script>
</head>
<body>
<h2>Processing your stuff...</h2>
<cfprogressbar name="mycfb" bind="cfc:process.getStatus()" interval="600" width="300" oncomplete="hideme" />
<cfset ajaxOnLoad("init")>
</body>
</html>
The hideme function simply uses the hide function to make the progress bar disappear. You can view this demo here: http://www.raymondcamden.com/demos/cfprogressbar/test3.cfm
Ok, so obviously this can be used to monitor the progress of some long running process. But you can also use it to provide feedback of a more manual process. Check out this example:
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
function init() {
ColdFusion.ProgressBar.updateStatus('mycfb', 0, 'Form Status')
}
$(document).ready(function() {
$("input").change(function() {
var complete = 0
if($("#name").val() != '') complete+=0.25
if($("#age").val() != '') complete+=0.25
if($("#email").val() != '') complete+=0.25
if($("#cheese").val() != '') complete+=0.25
if(complete == 1) {
msg = 'Form Complete!'
$("#save").attr("disabled",false)
} else msg = 'Form Status'
ColdFusion.ProgressBar.updateStatus('mycfb',complete,msg)
})
})
</script>
</head>
<body>
<cfprogressbar name="mycfb" width="300" />
<p>
<form>
Name: <input type="text" name="name" id="name"><br/>
Age: <input type="text" name="age" id="age"><br/>
Email: <input type="text" name="email" id="email"><br/>
Favorite Cheese: <input type="text" name="cheese" id="cheese"><br/>
<input type="submit" name="Save" id="save" disabled="true">
</form>
</p>
</body>
</html>
<cfset ajaxOnLoad("init")>
I've created a simple form with 4 fields in it. I use jQuery to bind to all the inputs on the page, specifically their change event handler. Whenever you change the values of one of the form fields, I check all 4 fields and create a 'completed' value that represents how much of the form you have done. If you have all four done, I update the status and re-enable the submit button. You can view this demo here: http://www.raymondcamden.com/demos/cfprogressbar/test4.cfm I think it's interesting as it provides the user feedback on how far they are in the process of finishing the form. It's overkill for just 4 fields, but you get the idea.
Archived Comments
Would be nice to have an easily implemented progress bar for file uploads NOT based on Flash.
This is a cool native feature, I wonder how difficult it would be to bind to cffile upload progress?
Have you seen the new cffileupload tag? It does this. Should this be my lunch time blog entry? :)
"Have you seen the new cffileupload tag? It does this. Should this be my lunch time blog entry? :)"
Yes please! ;]
Haven't seen that yet-- I'm going to look at the docs NOW!
How 'bout a demo?
Cool examples! One thing I noticed, when I click on a field and use one of the saved form field values (the values FireFox remembers) it doesn't recognize the field changed or update the progress bar. Could this be something to do with how jQuery registers a change event?
You mean if you pick from a drop down? If so, I haven't tested that. It should be a change event, right?
I ran into the same thing that Robert did. Whenever I used the autosuggest my browser provided and tabbed to the next input the progress bar did not advance.
I tested it out locally swapping the .blur event for the .change event and that corrects the issue.
Probably should have included the revised code: $(document).ready(function() {
$("input").blur(function() {
var complete = 0
if($("#name").val() != '') complete+=0.25
if($("#age").val() != '') complete+=0.25
if($("#email").val() != '') complete+=0.25
if($("#cheese").val() != '') complete+=0.25
if(complete == 1) {
msg = 'Form Complete!'
$("#save").attr("disabled",false)
} else{
msg = 'Form Status'
$("#save").attr("disabled",true)
}
ColdFusion.ProgressBar.updateStatus('mycfb',complete,msg)
})
})
added a line that disables the save button if you go back and remove the value from on of the fields. Totally unnecessary for a demo...
@Robert - I just tried FF and clicked on one of the saved values. The status bar updated for me, but only after the field loses focus (i.e., click on the next field). Same as typing it.
I tried your code Daniel:
http://cf9.coldfusionjedi.c...
It doesn't seem any better though. Not that it was bad for me before, but I don't see it updating quicker I mean.
The problem I was running into had nothing to do with speed. It just wouldn't update if I used autocompletion in FF or Safari. Not sure why... I guess that will remain a mystery.
While the AJAX features in CF have come a long way, the page sizes are still abominable. Your first example comes accross as 849kB in FireBug - and that's just a progress bar! I know that browser caching helps out here, but still - we have a fully loaded jQuery setup with the core library and jQuery UI, and a first-load page size for us is only around 275kB.
I don't know, maybe I'm just stingy with bandwidth. Does anyone else avoid teh CFAjax features because of the file sizes? Does anyone know a way to reduce the file sizes?
I think the CF Ajax features prove valuable for the new developer looking to get into Ajax. Thats where I started, and if it hadn't been for for the CF8 Ajax tags I probably would not have been exploring jQuery or Ext JS.
Yes, they are a bit bloated, but they are perfect for in house or small scale application development. I also think that with Ext JS 3.0 the CF9 stuff should speed up a bit more.
Another good addition.
The docs don't make this very clear but to use the style attribute just pass in all the options you want to use like this:
style="progresscolor:Blue;bgcolor:Orange;textcolor:Black;"
Though I wouldn't suggest using those colors! ;)
I would.
http://cf9.coldfusionjedi.c...
Heh
Hi,
Anyone here took time to see the load behind that !!
http://tools.pingdom.com/fp...
1 MB the whole think if the browser load all the stuff !!! For such cosmectifs it's a bit costly !
Regards
M B
Most of that load is the script base - which would be cached once you hit the site.
As I always say - it's a trade of. EXTREME ease of use over the slimmest performance. For most folks, that 1mg simply won't matter and won't even be noticed.
How did you do the styles in your example:
http://cf9.coldfusionjedi.c...
Here ya go: <cfprogressbar name="mycfb" bind="cfc:process.getStatus()" interval="600" width="300" style="progresscolor:Blue;bgcolor:Orange;textcolor:Black;"/>
Hello Ray, I think the article is great. I just have a few questions. I'm just unclear where to bind the progressbar.
Example: I have a function in a cfc that interacts with gmail with the cfimap feature. Sometimes the retrieval can be lengthy to say the least I want to use a progressbar to give the user a some type of feedback. Simply enough. How would I measure the total to the total loaded to report back to the progressbar?
Sorry - are you asking how to estimate the % done?
Help this padawan! :-)
Yes. All the examples I see bind to a getStatus function in a cfc. Do I have to place the procedures that I'm trying to do inside that getStatus function? I guess I'm confused on where the loader itself is placed and how the percentages are reported on a resizing or say In my case cfimap progress.
If that makes any sense.
Well the loader itself is on the front end - in your CFM. The CFC method, getStatus, is in the CFC. But the thing is - I can't tell you what to put there. It depends on your application logic. You mentioned using imap. Well if you are asking it to get all messages, you can't really measure that since it is an atomic operation (to CFML anyway).
i'm getting an error on the 4th example on this page. it says "Attribute validation error for the CFPROGRESSBAR tag. Either bind or duration needs to be defined. "
Looks like this is behavior that changed in the final release.
Anyone know if there is a special trick needed to load the ajax scripts from somewhere? I'm working in a hosted environment and since the progress won't display (even if a really simple example) I'm wondering if I need to load the .js files from somewhere special....
Try cfajaximport with the scriptSrc argument. You will need to FTP up a copy of the /CFIDE/scripts folder from your local install. (I may not be right on /CFIDE/scripts, but you get the idea.)
Very cool, thanks man! I was looking at that command but figured the host already had a mapping to the CFIDE directory and was pulling my hair out to make it work.
In case someone else gets stuck on this, here's the code I used, after posting the whole CFIDE/scripts/ directory to the "scripts" directory on my site:
<cfajaximport scriptSrc = "http://www.mydomainname.com...">
demos broken
Ah well, you can always download the code and run it yourself. :)
Hi Ray. I was interested in demo 4 because that's the same thing I'm trying to do... have the progress bar indicate how much of a form has been processed. But when I clicked your demo link the error said: Attribute validation error for the CFPROGRESSBAR tag. Either bind or duration needs to be defined.
Do you mind fixing it so I can review your demo? Thanks!
I had to fix it by pointing it to a JS bind. You can demo it by going to test4a.cfm, and see the code of the demo here:
http://pastebin.com/yPcqgJMt
I see. Very cool. However I was thinking you set the progress bar up so that it would show the status of the form being processed... not filled out. Is that something you've played with? I have yet to see an example and want to utilize cfprogressbar as opposed to jquery. Let me know. Thanks!
Then you would need to do a few things.
a) Make the form use Ajax to post it's data.
b) Have the CFM update a shared variable, like a session variable, as it works through it's stuff. So like if it's doing 5 things, it would update a variable like session.status and set it to 1, 2, etc.
c) Use an Ajax polling process to hit a file that simply outputs the value of the session variable. Your client side code would then update the progress bar.
Almost there. http://199.134.225.62/NW_PORTAL/test.cfm
That is leveraging the example on cf10's help on cfprogressbar. Now I just need to make it the actual processing time instead of a generic timed function.