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:
1 <html>
2
3 <head>
4 <link type="text/css" rel="stylesheet" href="theme/ui.all.css" />
5 <script src="jquery-1.3.1.js"></script>
6 <script src="jquery-ui-personalized-1.6rc6.js"></script>
7 <script>
8 $(document).ready(function() {
9 $("#progressbar").progressbar({value:69})
10 });
11 </script>
12 </head>
13
14 <body>
15
16 <div id="progressbar"></div>
17 </body>
18 </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:
2
3 <head>
4 <link type="text/css" rel="stylesheet" href="theme/ui.all.css" />
5 <script src="jquery-1.3.1.js"></script>
6 <script src="jquery-ui-personalized-1.6rc6.js"></script>
7 <script>
8 $(document).ready(function() {
9 $("#progressbar").progressbar({value:69})
10 });
11 </script>
12 </head>
13
14 <body>
15
16 <div id="progressbar"></div>
17 </body>
18 </html>
1 <!--- Imagine a cfquery here to get total donation --->
2 <cfset donations = 99>
3 <cfset perc = int(donations/399*100)>
4
5 <html>
6
7 <head>
8 <link type="text/css" rel="stylesheet" href="theme/ui.all.css" />
9 <script src="jquery-1.3.1.js"></script>
10 <script src="jquery-ui-personalized-1.6rc6.js"></script>
11 <script>
12 $(document).ready(function() {
13 <cfoutput>
14 $("##progressbar").progressbar({value:#perc#})
15 </cfoutput>
16 });
17 </script>
18 </head>
19
20 <body>
21
22 <h1>Buy Ray a PS3 Support Fund!</h1>
23
24 <div id="progressbar"></div>
25 </body>
26 </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:
2 <cfset donations = 99>
3 <cfset perc = int(donations/399*100)>
4
5 <html>
6
7 <head>
8 <link type="text/css" rel="stylesheet" href="theme/ui.all.css" />
9 <script src="jquery-1.3.1.js"></script>
10 <script src="jquery-ui-personalized-1.6rc6.js"></script>
11 <script>
12 $(document).ready(function() {
13 <cfoutput>
14 $("##progressbar").progressbar({value:#perc#})
15 </cfoutput>
16 });
17 </script>
18 </head>
19
20 <body>
21
22 <h1>Buy Ray a PS3 Support Fund!</h1>
23
24 <div id="progressbar"></div>
25 </body>
26 </html>
1 var currentValue = $("#pb").progressbar('option','value');
and setting then is:
1 $("#pb").progressbar('option','value',currentValue);
I quickly created a new demo that would let me increase and decrease the values:
1 <html>
2
3 <head>
4 <link type="text/css" rel="stylesheet" href="theme/ui.all.css" />
5 <script src="jquery-1.3.1.js"></script>
6 <script src="jquery-ui-personalized-1.6rc6.js"></script>
7 <script>
8
9 function less() {
10 var currentValue = $("#pb").progressbar('option','value');
11 currentValue--;
12 if(currentValue >= 0) $("#pb").progressbar('option','value',currentValue);
13 }
14
15 function more() {
16 var currentValue = $("#pb").progressbar('option','value');
17 currentValue++;
18 if(currentValue <= 100) $("#pb").progressbar('option','value',currentValue);
19 }
20
21 $(document).ready(function() {
22 $("#pb").progressbar({value:69})
23 $("#lessBtn").click(less);
24 $("#moreBtn").click(more);
25 });
26 </script>
27 </head>
28
29 <body>
30
31 <div id="pb"></div>
32 <input type="button" id="lessBtn" value="Less">
33 <input type="button" id="moreBtn" value="More">
34
35 </body>
36 </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:
2
3 <head>
4 <link type="text/css" rel="stylesheet" href="theme/ui.all.css" />
5 <script src="jquery-1.3.1.js"></script>
6 <script src="jquery-ui-personalized-1.6rc6.js"></script>
7 <script>
8
9 function less() {
10 var currentValue = $("#pb").progressbar('option','value');
11 currentValue--;
12 if(currentValue >= 0) $("#pb").progressbar('option','value',currentValue);
13 }
14
15 function more() {
16 var currentValue = $("#pb").progressbar('option','value');
17 currentValue++;
18 if(currentValue <= 100) $("#pb").progressbar('option','value',currentValue);
19 }
20
21 $(document).ready(function() {
22 $("#pb").progressbar({value:69})
23 $("#lessBtn").click(less);
24 $("#moreBtn").click(more);
25 });
26 </script>
27 </head>
28
29 <body>
30
31 <div id="pb"></div>
32 <input type="button" id="lessBtn" value="Less">
33 <input type="button" id="moreBtn" value="More">
34
35 </body>
36 </html>
1 <cfcomponent output="false">
2
3 <cfset this.name = "jqpb">
4
5 <cffunction name="onApplicationStart" returnType="boolean" output="false">
6 <cfreturn true>
7 </cffunction>
8
9 <cffunction name="onRequestStart" returnType="boolean" output="false">
10 <cfargument name="thePage" type="string" required="true">
11
12 <cfif structKeyExists(url, "reinit")>
13 <cfset onApplicationStart()>
14 </cfif>
15
16 <cfreturn true>
17 </cffunction>
18
19 <cffunction name="onError" returnType="void" output="false">
20 <cfargument name="exception" required="true">
21 <cfargument name="eventname" type="string" required="true">
22 <cfdump var="#arguments#"><cfabort>
23 </cffunction>
24
25 </cfcomponent>
Next, I created my CFM page that would handle running the slow process. I decides to use a simple timer system:
2
3 <cfset this.name = "jqpb">
4
5 <cffunction name="onApplicationStart" returnType="boolean" output="false">
6 <cfreturn true>
7 </cffunction>
8
9 <cffunction name="onRequestStart" returnType="boolean" output="false">
10 <cfargument name="thePage" type="string" required="true">
11
12 <cfif structKeyExists(url, "reinit")>
13 <cfset onApplicationStart()>
14 </cfif>
15
16 <cfreturn true>
17 </cffunction>
18
19 <cffunction name="onError" returnType="void" output="false">
20 <cfargument name="exception" required="true">
21 <cfargument name="eventname" type="string" required="true">
22 <cfdump var="#arguments#"><cfabort>
23 </cffunction>
24
25 </cfcomponent>
1 <cfsetting enablecfoutputonly="true">
2
3 <!--- start a process that takes 60 seconds. --->
4 <cfif not structKeyExists(application, "process")>
5 <cfset application.process = now()>
6 </cfif>
7
8 <!--- 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 --->
9 <cfset diff = dateDiff("s",application.process, now())>
10
11 <cfif diff gte 60>
12 <cfset structDelete(application, "process")>
13 <cfoutput>100</cfoutput>
14 <cfelse>
15 <cfset perc = diff/60*100>
16 <cfoutput>#int(perc)#</cfoutput>
17 </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:
2
3 <!--- start a process that takes 60 seconds. --->
4 <cfif not structKeyExists(application, "process")>
5 <cfset application.process = now()>
6 </cfif>
7
8 <!--- 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 --->
9 <cfset diff = dateDiff("s",application.process, now())>
10
11 <cfif diff gte 60>
12 <cfset structDelete(application, "process")>
13 <cfoutput>100</cfoutput>
14 <cfelse>
15 <cfset perc = diff/60*100>
16 <cfoutput>#int(perc)#</cfoutput>
17 </cfif>
1 <div id="pb"></div>
2 <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:
2 <input type="button" id="startBtn" value="Start">
1 $(document).ready(function() {
2 $("#pb").progressbar({value:0})
3 $("#startBtn").click(startProcess);
4 });
startProcess will now handle creating a timer:
2 $("#pb").progressbar({value:0})
3 $("#startBtn").click(startProcess);
4 });
1 var timer;
2
3 function startProcess() {
4 $("#startBtn").attr("disabled","disabled")
5 $("#startBtn").attr("value","Working...")
6 checkProcess()
7 timer = setInterval(checkProcess,1000)
8 }
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:
2
3 function startProcess() {
4 $("#startBtn").attr("disabled","disabled")
5 $("#startBtn").attr("value","Working...")
6 checkProcess()
7 timer = setInterval(checkProcess,1000)
8 }
1 function checkProcess() {
2 $.get('process.cfm',{},function (data,textStatus) {
3 $("#pb").progressbar('option','value',$.trim(data))
4 if(data == 100) {
5 clearInterval(timer)
6 $("#startBtn").removeAttr("disabled")
7 $("#startBtn").attr("value","Start")
8 }
9 })
10 }
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.
2 $.get('process.cfm',{},function (data,textStatus) {
3 $("#pb").progressbar('option','value',$.trim(data))
4 if(data == 100) {
5 clearInterval(timer)
6 $("#startBtn").removeAttr("disabled")
7 $("#startBtn").attr("value","Start")
8 }
9 })
10 }
Comment 1 written by Jonny Shaw on 26 February 2009, at 2:12 PM
Comment 2 written by tony petruzzi on 26 February 2009, at 2:46 PM
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.
Comment 3 written by Raymond Camden on 26 February 2009, at 2:47 PM
Comment 4 written by DRew on 27 February 2009, at 5:19 AM
Comment 5 written by Henry Ho on 27 February 2009, at 1:03 PM
Comment 6 written by Raymond Camden on 27 February 2009, at 1:33 PM
Comment 7 written by Chris Dawes on 2 March 2009, at 7:03 PM
It makes me laugh how many ColdFusion developers use jQuery to do things that are built right into the ColdFusion install.
Comment 8 written by Raymond Camden on 2 March 2009, at 7:08 PM
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. ;)
Comment 9 written by Daniel S on 6 March 2009, at 12:13 PM
Comment 10 written by Raymond Camden on 6 March 2009, at 12:28 PM
<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.
Comment 11 written by Daniel S on 6 March 2009, at 1:22 PM
<cf_wrapper loadjq="true">
<cfhtmlhead text=" [all your jQuery functions including more() and less()]">
page content
</cf_wrapper>
Comment 12 written by Raymond Camden on 6 March 2009, at 3:37 PM
Comment 13 written by Aaron on 26 March 2009, at 8:57 AM
<cfsavecontent variable="jquery">
[all your jQuery functions including more() and less()]</cfsavecontent>
<cfhtmlhead text="#jquery#">
Comment 14 written by Meenakshi on 19 January 2010, at 2:03 PM
Comment 15 written by Raymond Camden on 19 January 2010, at 2:06 PM
Comment 16 written by Meenakshi on 19 January 2010, at 2:43 PM
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
Comment 17 written by Raymond Camden on 19 January 2010, at 2:46 PM
Comment 18 written by Meenakshi on 19 January 2010, at 2:56 PM
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
Comment 19 written by Raymond Camden on 19 January 2010, at 3:58 PM
Comment 20 written by Meenakshi on 20 January 2010, at 10:29 AM
I am on CF8
Thanks
Comment 21 written by Meenakshi on 20 January 2010, at 10:32 AM
I test your last example (http://www.coldfusionjedi.com/index.cfm/2009/2/26/...) on IE and its not working properly....so I think it is IE issue.
Comment 22 written by Raymond Camden on 20 January 2010, at 10:34 AM
Comment 23 written by Meenakshi on 20 January 2010, at 12:02 PM
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
Comment 24 written by Raymond Camden on 20 January 2010, at 12:04 PM
[Add Comment] [Subscribe to Comments]