I've finally gotten around to writing the follow up to my last entry on building a simple calendar in ColdFusion. I mentioned that the next entry was going to focus on making the CSS a bit more configurable, but that's boring. I'll do that next. What folks really want is an example of sending data to the custom tag. So let's get to it.
First off - lets talk about calendar data. Our custom tag is meant to be abstract, but as we all know, applications are not (typically) abstract. In fact, this was the main reason why I took so long to write these calendar write ups. (The first entry was actually written back in 2005.)
So I decided to just say screw it. My tag will make some basic assumptions if you want to use it and that's that. The assumptions are pretty simple though.
First off - your event data must be a query. Not a list. Not an array. Not a cow bell. But a query.
Secondly - your query must have a title, date, and link column. These columns will be what the custom tag will look for when displaying data.
So I don't think this is too bad. If your events data doesn't look exactly like this, you can always modify your query a bit to rename the columns. Plus, maybe I'll figure out a smart way to make this easier?
Let's jump into our custom tag. The first thing I'm going to do is add a new cfparam to create a default for our event data:
<cfparam name="attributes.events" default="">
I created a simple string for the events attribute. Later on I'll check to see if events is a query.
If you remember, we looped over each day of the month. Here was the original code:
<cfloop index="x" from="1" to="#days#">
<cfif x is day(attributes.today) and attributes.month is month(attributes.today) and attributes.year is year(attributes.today)>
<cfoutput><td class="cell_today"></cfoutput>
<cfelse>
<cfoutput><td class="cell"></cfoutput>
</cfif>
<cfoutput>#x#</cfoutput>
<cfoutput></td></cfoutput>
Now I'm going to add code that will check my query for events. I'll post the complete code, then I'll explain it:
<!--- check for stuff for today --->
<cfif isQuery(attributes.events)>
<cfset thisDate = createDate(attributes.year, attributes.month, x)>
<cfquery name="todaysEvents" dbtype="query">
select title, link
from attributes.events
where [date] >= <cfqueryparam cfsqltype="cf_sql_date" value="#thisDate#">
and [date] < <cfqueryparam cfsqltype="cf_sql_date" value="#dateAdd("d", 1, thisDate)#">
</cfquery>
<cfif todaysEvents.recordCount>
<cfoutput>
<cfloop query="todaysEvents">
<cfif len(link)><a href="#link#"></cfif>#title#<cfif len(link)></a></cfif><br />
</cfloop>
</cfoutput>
</cfif>
</cfif>
The first thing I do is check if attributes.event is actually a query. If it - I then create a date object for the current date.
Next I use query of query to look for events that occur within that date. Note how I add one to the date value to create the second boundary of my where clause.
Last - I check to see if my query of query returned anything. If it did, I simply output over the events.
Pretty simple, right? What's nice is that the custom tag will continue to work without event data if you want a simple calendar display. But now it supports passing in events and having them displayed (and linked as well).
Ok - so as I mentioned - your database probably won't match the structure the custom tag will use. What if there was a nice way around that? Turns out there is.
First I'm going to add 3 new attributes to my custom tag:
<cfparam name="attributes.datecolumn" default="date">
<cfparam name="attributes.titlecolumn" default="title">
<cfparam name="attributes.linkcolumn" default="link">
These all define defaults for the date, title, and link columns for my query. Now lets see how we can use this in the code:
<cfquery name="todaysEvents" dbtype="query">
select #attributes.titlecolumn# as title, #attributes.linkcolumn# as link
from attributes.events
where [#attributes.datecolumn#] >= <cfqueryparam cfsqltype="cf_sql_date" value="#thisDate#">
and [#attributes.datecolumn#] < <cfqueryparam cfsqltype="cf_sql_date" value="#dateAdd("d", 1, thisDate)#">
</cfquery>
This is the query we used before to determine if we had any events for today. However - now my column names are dynamic. I use aliases so I end up with the same column names. Now you can pass any query to the tag and simply tell it which columns to use for the title, link, and date columns.
I've attached the latest copy to this blog entry. In the follow up, I'll talk about how we can make the CSS a bit prettier. No, I haven't suddenly sprouted CSS genes. Instead I'll discuss how the custom tag can be told to rely on external CSS items instead. Enjoy.
Archived Comments
Just wanted to thank you for your initial example of the calendar. It helped me build the calendar for: http://www.zaperone.com/kno... A website that is currently in the testing phase, not the most beautiful masterpiece but i figured out how to integrate some CSS to make the design a bit nicer. Also note how you can navigate between months and it keeps track of it in the url. So theoretically it goes forever. This calendar also loads events from a database added by teachers, or school groups.
SO THANKS SO MUCH, for giving me the start i needed. Tell me what you think if you can. :)
Brad, glad it was helpful!
Thank you for this awesome tutorial! I was looking for an existing calendar app, but everything i found was tricky to handle or to implement in existing projects. and i won't use the "build in" flash calendar.
Was the follow up with CSS ever posted?
I've made a modified version of this calendar using CSS and JQuery code I found here: http://www.stefanoverna.com...
Ah no, since this was from 2 years ago, I'd say no. :)
Raymond,
Was interested in using your calendar as a starting point for a calendar app we are developing for a site, and unfortunately your download link throws a 404 - could you resolve that so I can get the complete calendary goodness you provided at one time? Thanks!
Sorry - I moved web sites recently and forgot to copy over the enclosures.
Ray,
First off, thanks for the work you do and the information you share! I am a beginner with ColdFusion and articles like this are priceless.
I had a question about passing events to the calendar, and the question is; how do I do it? My work with ColdFusion has mainly been with databases. I don't understand where the attributes, such as *attribute.event*, are coming from, and I don't understand queering a query. I would think if you were not using a database for a query then you would use a form to populate the event information, but then you would have *form.events* rather than *attribute.event*, at least I think you would.
Hope that's clear
Well the idea was that you had events in your database already and you selected them in a query. That query, let's call it foo, would be passed to the custom tag in the events argument. This is covered in the previous entries.
I went through all three entries on the calendar again and I did manage to find out why you are using *attribute.event*. But I still do not see how to get my query information to pass to the custom tag. Please forgive me if I am being a dumb dumb but I just don't see what I am missing. This is the code I have:
<body>
<cfquery name="events" datasource="employeeApplicants">
SELECT title, date, link
FROM Events
</cfquery>
<cfoutput query="events">
<cf_calendar title="#title#" date="#date#" link="#link#">
</cfoutput>
</body>
I have six entries in the database and when I view my page I see six calenders with no events inserted in any of them.
Thanks again for your help with this.
You only want to run cf_calendar one time, and you pass the events query to it. Did you download the attached file? It shows an example. Given that your query is called events, you would do this:
<cf_calendar events="#events#" linkcolumn="link2">
Again - try the downloaded attachments from the blog posts.
I downloaded the file and it was a big help, however now only one event is appearing in the calendar. I though that maybe i needed to add a cfloop to my output but that doesn't seem to be helping.
My current code:
<body>
<cfquery name="events" datasource="employeeApplicants">
SELECT title, date, link
FROM Events
</cfquery>
<cfoutput query="events">
<cfset events = queryNew("date,title,link")>
<cfset queryAddRow(events)>
<cfset querySetCell(events, "date", #date#)>
<cfset querySetCell(events, "title", #title#)>
<cfset querySetCell(events, "link", #link#)>
</cfoutput>
<cf_calendar events="#events#" linkcolumn="link">
</body>
The purpose of the queryNew code in my example was to make a 'fake' query so you could run the demo as is. You don't need this code because you have a real query. Just pass your events query as is.
like this?:
<cfquery name="events" datasource="employeeApplicants">
SELECT title, date, link
FROM Events
</cfquery>
<cf_calendar events="#events#" linkcolumn="link">
Yes.
When I ran your 'fake' query it worked perfect. When i run the code pasted in my last comment the calendar is blank, with no events. I double checked that the query works with a CFDUMP. Any suggestions?
The calendar defaults to displaying this month. Do you have events in there for this month?
All six events in the database are in the month of May. Also when I had
_<cfset events = queryNew("date,title,link")>
<cfset queryAddRow(events)>
<cfset querySetCell(events, "date", #Date#)>
<cfset querySetCell(events, "title", #Title#)>
cfset querySetCell(events, "link", #Link#)>_
included in my code the last event in the database was placed in the calendar correctly, but only that one event. Not sure if that helps pinpoint what going on.
Not sure then. Are the years right for the values? It checks the year too. Have you tried *any* debugging at all? I believe you said you were new to CF, but you should try some basic debugging just to see if you can figure out the issue.
Not anything beyond the DUMP that I did. I will look into further debugging. Thanks again for all your help, your quick responses are greatly appreciated.