A reader pinged me yesterday with a simple problem that I thought would be good to share on the blog. He had a query of events that he wanted to use with jQuery UI's Accordion control. The Accordion control simply takes content and splits into various "panes" with one visible at a time. For his data, he wanted to split his content into panes designated by a unique month and year. Here is a quick demo of that in action.
I began by creating a query to store my data. I created a query with a date and title property and then add up to three "events" over the next twelve months. I specifically wanted to support 0 to ensure my demo handled noticing months without any data.
<!---
Create some fake data with dates
--->
<cfscript>
q = queryNew("date,title");
for(i=1; i<12; i++) {
//for each month, we add 0-3 events (some months may not have data)
toAdd = randRange(0, 3);
for(k=0; k<toAdd; k++) {
newDate = dateAdd("m", i, now());
queryAddRow(q, {date:newDate, title:"Item #i##k#"});
}
}
</cfscript>
To handle creating the accordion, I had to follow the rules jQuery UI set up for the control. Basically - wrap the entire set of data in a div, and separate each "pane" with an h3 and inner div. To handle this, I have to know when a new unique month/year "block" starts. I store this in a variable, lastDateStr, and just check it in every iteration over the query. I also need to ensure that on the last row I close the div.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI Accordion - Default functionality</title>
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
<script>
$(function() {
$( "#accordion" ).accordion();
});
</script>
</head>
<body>
<div id="accordion">
<cfset lastDateStr = "">
<cfloop query="q">
<cfset thisDateStr = month(date) & "/" & year(date)>
<!--- Is this datestr different? --->
<cfif thisDateStr neq lastDateStr>
<!--- We only 'close' a div if not on the first iteration --->
<cfif currentRow neq 1>
</div>
</cfif>
<cfoutput>
<h3>#thisDateStr#</h3>
</cfoutput>
<div>
<cfset lastDateStr = thisDateStr>
</cfif>
<cfoutput>
#title#<br/>
</cfoutput>
<cfif currentRow is recordCount>
</div>
</cfif>
</cfloop>
</div>
</body>
</html>
And the end result:
So, not rocket science, but hopefully helpful to someone. Here is the entire template if you want to try it yourself.
<!---
Create some fake data with dates
--->
<cfscript>
q = queryNew("date,title");
for(i=1; i<12; i++) {
//for each month, we add 0-3 events (some months may not have data)
toAdd = randRange(0, 3);
for(k=0; k<toAdd; k++) {
newDate = dateAdd("m", i, now());
queryAddRow(q, {date:newDate, title:"Item #i##k#"});
}
}
</cfscript>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI Accordion - Default functionality</title>
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
<script>
$(function() {
$( "#accordion" ).accordion();
});
</script>
</head>
<body>
<div id="accordion">
<cfset lastDateStr = "">
<cfloop query="q">
<cfset thisDateStr = month(date) & "/" & year(date)>
<!--- Is this datestr different? --->
<cfif thisDateStr neq lastDateStr>
<!--- We only 'close' a div if not on the first iteration --->
<cfif currentRow neq 1>
</div>
</cfif>
<cfoutput>
<h3>#thisDateStr#</h3>
</cfoutput>
<div>
<cfset lastDateStr = thisDateStr>
</cfif>
<cfoutput>
#title#<br/>
</cfoutput>
<cfif currentRow is recordCount>
</div>
</cfif>
</cfloop>
</div>
</body>
</html>