I mentioned in yesterday's blog posting on the Sortable plugin that I was taking a closer look at jQuery UI and how I could integrate it with ColdFusion. I've taken a closer look at another of the widgets and thought I'd share my findings. (Also please note a personal request at the end.)
The jQuery Progress Bar is pretty much what you would expect it to be. A bar. That shows progress. Yes, not rocket science, I know. Here is an example using the Swanky Purse theme (still my favorite theme):

The code behind this is ridiculously simple:
<html>
<head>
<link type="text/css" rel="stylesheet" href="theme/ui.all.css" />
<script src="jquery-1.3.1.js"></script>
<script src="jquery-ui-personalized-1.6rc6.js"></script>
<script>
$(document).ready(function() {
$("#progressbar").progressbar({value:69})
});
</script>
</head>
<body>
<div id="progressbar"></div>
</body>
</html>
Like other widgets, I have to remember to include my CSS along with the JavaScript. Once I've done that, I simply tell the plugin to turn a particular ID into a progress bar. The code above uses a hard coded value of 69. Progress bars uses a value system based on a percentage from 0 to 100. You can see this in action here.
Obviously a static progress bar isn't too exciting, and when I was mentally preparing this blog post in my head this is the part where I was going to immediately jump into creating a dynamic progress bar. However - it occurred to me that a static bar isn't exactly useless either. Imagine a case where you want to mark the progress of something that takes place over a few days, or weeks. For example, a donation drive. You may get one donation per day. It would be kind of silly to build an auto-updating Ajax-driven progress bar for something that won't likely change for a web site visitor. At the same time, you don't want to have to build a new graphic as the donation drive goes on. A static progress bar would be a great - and simple - way to handle this. Consider:
<!--- Imagine a cfquery here to get total donation --->
<cfset donations = 99>
<cfset perc = int(donations/399*100)>
<html>
<head>
<link type="text/css" rel="stylesheet" href="theme/ui.all.css" />
<script src="jquery-1.3.1.js"></script>
<script src="jquery-ui-personalized-1.6rc6.js"></script>
<script>
$(document).ready(function() {
<cfoutput>
$("##progressbar").progressbar({value:#perc#})
</cfoutput>
});
</script>
</head>
<body>
<h1>Buy Ray a PS3 Support Fund!</h1>
<div id="progressbar"></div>
</body>
</html>
This is pretty much the exact same code as above but now I have a bit of code to determine the current percentage. My JavaScript code makes use of this value and I added a label over the progress bar so it was a bit more obvious. You can see this in action here.
So what if you do want a dynamic progress bar? As you can imagine the docs go into detail about what events and methods you can use with the progress bar. Getting the current value is as easy as:
var currentValue = $("#pb").progressbar('option','value');
and setting then is:
$("#pb").progressbar('option','value',currentValue);
I quickly created a new demo that would let me increase and decrease the values:
<html>
<head>
<link type="text/css" rel="stylesheet" href="theme/ui.all.css" />
<script src="jquery-1.3.1.js"></script>
<script src="jquery-ui-personalized-1.6rc6.js"></script>
<script>
function less() {
var currentValue = $("#pb").progressbar('option','value');
currentValue--;
if(currentValue >= 0) $("#pb").progressbar('option','value',currentValue);
}
function more() {
var currentValue = $("#pb").progressbar('option','value');
currentValue++;
if(currentValue <= 100) $("#pb").progressbar('option','value',currentValue);
}
$(document).ready(function() {
$("#pb").progressbar({value:69})
$("#lessBtn").click(less);
$("#moreBtn").click(more);
});
</script>
</head>
<body>
<div id="pb"></div>
<input type="button" id="lessBtn" value="Less">
<input type="button" id="moreBtn" value="More">
</body>
</html>
I've added two buttons, Less and More, each of which will run a simple function to either increase or decrease the progress bar value. I added a bit of logic to ensure I don't go below 0 or above 100. You can see this demo here.
Ok, so time to get sexy. A progress bar is really useful for monitoring a slow process. You can imagine something like image resizing. Shrinking a large directory of images could take a while and it would be nice to present a UI to the user so they can see the progress of the slow process. I designed a simple ColdFusion demo that will hopefully demonstrate how you could do this.
First, I added an Application.cfc just to enable Application variable support:
<cfcomponent output="false">
<cfset this.name = "jqpb">
<cffunction name="onApplicationStart" returnType="boolean" output="false">
<cfreturn true>
</cffunction>
<cffunction name="onRequestStart" returnType="boolean" output="false">
<cfargument name="thePage" type="string" required="true">
<cfif structKeyExists(url, "reinit")>
<cfset onApplicationStart()>
</cfif>
<cfreturn true>
</cffunction>
<cffunction name="onError" returnType="void" output="false">
<cfargument name="exception" required="true">
<cfargument name="eventname" type="string" required="true">
<cfdump var="#arguments#"><cfabort>
</cffunction>
</cfcomponent>
Next, I created my CFM page that would handle running the slow process. I decides to use a simple timer system:
<cfsetting enablecfoutputonly="true">
<!--- start a process that takes 60 seconds. --->
<cfif not structKeyExists(application, "process")>
<cfset application.process = now()>
</cfif>
<!--- app.process is a timestamp, determine how much of the 60 seconds we have finished. if 60 or more, report 100 and kill the process --->
<cfset diff = dateDiff("s",application.process, now())>
<cfif diff gte 60>
<cfset structDelete(application, "process")>
<cfoutput>100</cfoutput>
<cfelse>
<cfset perc = diff/60*100>
<cfoutput>#int(perc)#</cfoutput>
</cfif>
This code will look for an Application variable named process. If it doesn't exist, it will be created and set to the current time.
I then check the difference in seconds from the variable. If less then 60, I output the the percentage value of the time passed. If greater than 60, I output 100 and remove the variable. (Note - this code would need to have locking added to be properly single threaded!) I tested this by hitting it in my browser and reloading. I watched the value go slowly from 0 to 100 and then back again. Once I was sure it worked ok I then moved on to the front end.
I began by adding a button beneath my progress bar:
<div id="pb"></div>
<input type="button" id="startBtn" value="Start">
I then modified my document.ready to initialize the progress bar to 0 and listen for the button's click event:
$(document).ready(function() {
$("#pb").progressbar({value:0})
$("#startBtn").click(startProcess);
});
startProcess will now handle creating a timer:
var timer;
function startProcess() {
$("#startBtn").attr("disabled","disabled")
$("#startBtn").attr("value","Working...")
checkProcess()
timer = setInterval(checkProcess,1000)
}
I do a few things here besides just starting the timer. I disable and change the value of the start button. I run checkProcess immediately, and then set an interval for the function.
checkProcess handles doing an Ajax call to my CFM above:
function checkProcess() {
$.get('process.cfm',{},function (data,textStatus) {
$("#pb").progressbar('option','value',$.trim(data))
if(data == 100) {
clearInterval(timer)
$("#startBtn").removeAttr("disabled")
$("#startBtn").attr("value","Start")
}
})
}
I run a simple get and then examine the result. I set the progress bar to the number returned, and if the value was 100, I handle killing the timer and resetting the button.
You can see this in action here. Note - the file you run is a CFM file but I don't actually use ColdFusion in the view at all. It should have been an HTML file. (I hope I can be forgiven for defaulting all my files to ColdFusion files out of habit!)
p.s. I've been doing a lot of jQuery posts lately. I hope my readers are enjoying them. I'm trying my best to tie each post to something ColdFusion related as well. If anyone has feedback on this, let me know via email. I'm hoping these articles are helpful to those new to jQuery, or perhaps looking for ways to integrate jQuery more with ColdFusion.
Archived Comments
Good Job, i have started getting into jQuery, its a very good tool for giving your sites a bit of style.
EXCELLENT RAY!!!!!
I've been playing with the uploader and the sorter plugins for a bit now and these are the best articles about them I seen.
side note: in production you want to use the session scope rather then the application scope.
@tony: I disagree. What scope you use has nothing to do with jQuery, it has to do with the business need - or what the task is in general. The applications cope may make sense - or the session scope. It is outside the context of this blog entry. (My opinion. ;)
Love the jQuery posts. I feel like I'm late to the party on jQuery, but better late than never. In bj times (before jQuery) I knew how to do all of this stuff using AJAX, but the code would be so cumbersome and not browser-compatible that it generally wasn't worth the effort. That's changed for the better and so has everything that I've developed.
hum.. time to add some syntax coloring to your Code section. It is getting hard to read...
Yep - when I reveal my new blog skin.
Why don't you just use the progress-bar included in ColdFusion using ExtJS?
It makes me laugh how many ColdFusion developers use jQuery to do things that are built right into the ColdFusion install.
What progress bar? There is a cfslider, but that isn't a progress bar.
There are many reasons why a person may choose to use jQuery over CFAjax, and vice versa. I've blogged many times on the stuff built into CF, and now I'm blogging about jQuery. I would hope that my readers are smart enough to figure out which is best for them. (Laughable or not. ;)
Methinks you avoided using CF and <cfoutput> so that you wouldn't have to escape the ID selector everytime they are used in jQuery (which is a lot, usually). And what is best practice for inserting jQuery code in a content page if you have already imported a header template that includes <html><head>....</head>. You can't the the jQuery into the <head> without using <cfhtmlhead> which requires you to escape double quotes or single quotes so many times you get lost? Got to be a better way...
If you have already output then yes, cfhtmlhead is the only way. Generally I build my sites with a custom tag wrapper:
<cf_wrapper>
page content
</cf_wrapper>
And I tell the custom tag to use jQuery when approprite:
<cf_wrapper loadjq="true">
page content
</cf_wrapper>
But to be honest, the jQuery library is so dang small I'd probably just include it everywhere, unless it really was just used in a small set of my site.
Thanks for your response. Maybe I didn't describe the dilemma clearly. I'm talking about page-specific jQuery code like you wrote in this blog post. Not necessarily the jQuery library itself. You wouldn't want to include the more() and less() functions and the document.ready() code on every page on your site, since it is meaningful only to this page. So, are you saying, if this were a real page, you would do this:
<cf_wrapper loadjq="true">
<cfhtmlhead text=" [all your jQuery functions including more() and less()]">
page content
</cf_wrapper>
I don't believe it needs to be in the HEAD at all. I believe it will work fine in any simple script block.
Doesn't need to be in the head, but if you want it to be in the head use cfsavecontent.
<cfsavecontent variable="jquery">
[all your jQuery functions including more() and less()]</cfsavecontent>
<cfhtmlhead text="#jquery#">
$.get function is not working in IE......any idea how can I make this work in IE?
Odd. How does it fail? Try using a network monitor like Charles or ServiceCapture.
I download the ServiceCapture. It is displaying call to file in $.get in Firefox but not in IE.
My code look like this
function checkProcess(FlattenID) {
$.get('/path/progress.cfm?FlattenID=' + FlattenID,{},function (data,textStatus) {
$("#progressbar").progressbar('option','value',$.trim(data));
$("#progressbarText").html(data + '% Complete');
if(data == 100) {
clearInterval(timer);
}
})
}
Its working fine in Chrome too but not in IE
I wonder if IE is caching it - but if it did, it would still run the result at least. You got me there. Maybe look in the corner of the browser where IE likes to hide errors.
Ray,
In IE Developer Tool...it is giving error
Exception thrown and not caught
cfajax.js line 90 character 1
i didn't see any other error
Thanks
Meenakshi
That's in the libraries shipped with CF. Odd. What is line 90 of that file? Are you on the latest (patched) version of CF8, or CF9?
Line 90 says : throw msg;
I am on CF8
Thanks
Ray,
I test your last example (http://www.coldfusionjedi.c... on IE and its not working properly....so I think it is IE issue.
Why am I not surprised? :)
Ray,
I finally used CF8 ajaxproxy method to get value from CFC. But I would love to find a solution of $.get in IE
Thanks for your help
Being on a Mac (primarily), I don't often test in IE. Not sure I can help here.
Hi!
I have a huge cfm that runs a lot of querys. Is it possible to use this progress bar to avoid timeout and display some kind of time remaining ?
It may be possible if your script outputs the right responses and makes use of cfflush.