Have you ever found yourself in a situation where you needed to implement some new feature using one scripting language within a template that is written in a different scripting language, or possibly no scripting language at all (pure HTML)? Of course, you could just turn the pure HTML template into a CFML template, and at times that is indeed the best route to go, as long as breaking every link pointing back to this page is acceptable. If the target template is written in another scripting language, however, transforming it into a CFML template would probably not be a viable option. Ah, but there is at least one way to still be able to integrate your Coldfusion functionality into any template (HTML OR those written in other scripting languages): Leveraging the power of Ajax.
Typically the first route our minds go when asked to integrate two discrete templates is to look for a way to "include" one within the other. HTML does provide a type of inclusion ability (called 'Server Side Includes' (SSI)), but this approach is typically used to include other HTML documents and would be challenging to make work with a Coldfusion template. Other scripting languages have the ability to include other templates written in the same language, but getting them to play nicely including Coldfusion would also prove challenging. You could also go the route of using an IFRAME within your template, but this approach also presents limitations and hurdles that you may not wish to deal with. The simple solution, and the simplest approach to making it work, again, is Ajax.
The Scenario
In this scenario, the target template is a static HTML site and the code you want to integrate is written in Coldfusion. Our goal is to dynamically display the contents of an upcoming events database table in our Events section of the company's HTML home page. Here's how I approached it:
1. Create a target within the static HTML page where you want the event data to appear. In my case, I created this bit of HTML:
<div id="eventTarg"></div>
2. Create a CFM template to act as your data provider. Mine is called eventAjax.cfm, and contains several different methods since it is used by other applications as well;
3. "Wire up" your HTML page with your favorite Javascript library. Mine (currently) is Prototype, so my code snippets will reflect Prototype's syntax, but you should be able to easily relate it to your favorite flavor
That's it! Now, some details...
The Data Provider
Some may feel the need to write full blown web services to provide data to the consumer/caller, but personally I feel that most times this is major overkill when a RESTful approach works beautifully. Here is what my eventAjax.cfm looks like, showing only a couple of possible methods:
<!--- suppress any and all output except what we explicitly allow via cfoutput tags... --->
<cfsetting enablecfoutputonly="true" showdebugoutput="false" />
<!--- our switch expression value. this determines what action will take place --->
<cfparam name="meth" default="" />
<cfswitch expression="#meth#">
<cfcase value="getEventLinks">
<!--- retrieving all events that have not yet ended and are published --->
<cfquery name="qryGetLiveEvents" datasource="#dsn#">
select id,name,descrip,eventfromdate,eventtodate
from registrationevents
where <cfqueryparam value="#now()#" cfsqltype="cf_sql_date" /> <= eventToDate
AND publish = <cfqueryparam value="1" cfsqltype="cf_sql_tinyint" />
order by eventFromDate
</cfquery>
<!--- by default, create this return value... --->
<cfset links = "no events currently scheduled" />
<cfif qryGetLiveEvents.recordcount gt 0><!--- if we have records, loop over them and create the 'links' content --->
<cfoutput>
<cfsavecontent variable="links">
<p id="eventlinks" class="eventlink">
<cfloop query="qryGetLiveEvents">
<a href="registration.cfm?eventid=#id#" target="_blank">#name#</a><span class="eventText"> <em>(from #dateformat(eventfromdate,"mm/dd/yyyy")# to #dateformat(eventtodate,"mm/dd/yyyy")#)</em><br>#descrip#</span><br><br>
</cfloop>
</p>
</cfsavecontent>
</cfoutput>
</cfif>
<cfoutput>#links#</cfoutput>
</cfcase>
<cfcase value="updatePublish">
<cfparam name="eventid" default=""/>
<cfif not isnumeric(eventid)>
<cfoutput>INVALID EVENT ID</cfoutput>
<cfabort>
</cfif>
<cfquery name="qryUpdatePublish" datasource="#dsn#">
UPDATE registrationevents
SET publish = <cfqueryparam value="#newval#" cfsqltype="cf_sql_tinyint" />
WHERE id=<cfqueryparam value="#eventid#" cfsqltype="cf_sql_integer" />
</cfquery>
</cfcase>
<cfdefaultcase>
<cfoutput>UNAUTHORIZED ACCESS</cfoutput>
</cfdefaultcase>
</cfswitch>
<!--- helper functions can reside below this line... --->
Reading through this code, it is pretty self-explanatory. Basically, when the ajax call is made, only values explicitly output using CFOUTPUT tags will be returned to the caller. My "getEventLinks" method is returning a pre-formed block of html that my client side javascript will simply dump into its target div.
Wiring up the HTML
Wiring up our HTML page consists of doing only two things:
1. include your javascript library;
2. create a function to be executed after the html has fully loaded;
My step 1 line looks like this:
...
<title>Company Name Here</title>
<script type="text/javascript" src="js/prototype.js"></script>
....
</html>
Step 2 is inline script written at the bottom of the template, just within the <body> tag. I don't believe it HAS to reside within the <body> tag, but that's where i chose to put it. It looks like this:
document.observe("dom:loaded", function() {
params = new Hash();
params.set("meth","getEventLinks");
new Ajax.Updater('eventTarg','eventAjax.cfm',{parameters:params,method:'post'});
});
</script>
That's it! Now, everytime the page is loaded, Prototype will execute an ajax call to the template specified and will stuff the results into the targeted div 'eventTarg'. If no qualifying events are found, we'll simply get the phrase "no events currently scheduled".
Here is a screenshot of the html page loaded, using Firebug to see the ajax call that was made and the value returned:





