You are viewing documentation for v1.0.x. Change

Tutorial: Wheels, AJAX, and You

Using Wheels to develop web applications with AJAX features is a breeze. You have several options and tools at your disposal, which we'll cover in this chapter.

Wheels was designed to be as lightweight as possible, so this keeps your options fairly open for developing AJAX features into your application. We will cover two different approaches in this chapter:

  1. "Do it yourself" method with a fresh out-of-the box install of Wheels
  2. "Plugins" method with the help of a couple different plugins built by community members

While there are several flavors of JavaScript libraries out there with AJAX support, we will be using the jQuery framework in this tutorial. Let's assume that you are fairly familiar with the basics of jQuery and know how to set it up.

For this tutorial, let's create the simplest example of all: a link that will render a message back to the user without refreshing the page.

Approach #1: "Do It Yourself"

In this example, we'll wire up some simple JavaScript code that calls a Wheels action asynchronously. All of this will be done with basic jQuery code and Wheels functionality.

First, let's create a link to a controller's action in a view file, like so:

<cfoutput>

<!--- View code --->
<h1></h1>
<p></p>

#linkTo(text="Alert me!", controller="say", action="hello", id="alert-button")#

</cfoutput>

That piece of code by itself will work just like you expect it to. When you click the link, you will load the hello action inside the say controller.

But let's make it into an asynchronous request. Add this JavaScript (either on the page inside script tags or in a separate .js file included via javaScriptIncludeTag()):

// Listen to the "click" event of the "alert-button" link and make an AJAX request
$("#alert-button").click(function() {
$.ajax({
type: "POST",
url: $(this).attr("href"), // Outputs "/say/hello"
dataType: "json",
success: function(response) {
$("h1").html(response.message);
$("p").html(response.time);
}
});
return false; // keeps the normal request from firing
});

With that code, we are listening to the click event of the hyperlink, which will make an asynchronous request to the hello action in the say controller.

Note that the success block inserts keys from the response into the empty h1 and p blocks in the calling view. (You may have been wondering about those when you saw the first example. Mystery solved.)

The last thing that we need to do is implement the say/hello action. Note that the request expects a dataType of JSON. By default, Wheels controllers only generate HTML responses, but there is an easy way to generate JSON instead using Wheels's renderText() function along with CF's SerializeJSON() function:

<!--- Controller code --->
<cffunction name="hello">
<!--- Prepare the message for the user --->
<cfset greeting = {}>
<cfset greeting["message"] = "Hi there">
<cfset greeting["time"] = Now()>

<!--- If your request is an AJAX request, respond with the CFML SerializeJSON function --->
<cfif isAjax()>
<cfset renderText(SerializeJSON(greeting))>
</cfif>

<!--- Otherwise, let Wheels do its normal HTML response (the view file at `views/say/hello.cfm`) --->
</cffunction>

Notice the lines where we're setting greeting["message"] and greeting["time"]. Due to the case-insensitive nature of ColdFusion, we recommend setting variables to be consumed by JavaScript using bracket notation like that. If you do not use that notation (i.e., greetings.message and greetings.time), your JavaScript will need to reference those keys from the JSON as MESSAGE and TIME (all caps). Unless you like turning caps lock on and off, you can see how that would get annoying after some time.

Assuming you already included jQuery in your application and you followed the code examples above, you now have a simple AJAX-powered web application built on Wheels. After clicking that Alert me! link, your say controller will respond back to you the serialized message via AJAX.

Well, that wasn't so bad, but there is an easier way. Enter Wheels plugins.

Approach #2: Plugins

One of the many benefits of Wheels is the Plugin Directory. Currently, there are 2 plugins that will ease your AJAX development:

  • Remote Form Helpers adds a set of CFMJS functions (CFML + JavaScript) as well as a set of new form helpers that work exclusively with AJAX.

  • Provides (ColdFusion 9 only) enables Wheels actions to respond to your requests with XML, JSON, and other formats like PDF, Excel, CSV, and other custom types.

Remote Form Helpers Plugin

By itself, the Remote Form Helpers plugin will add another type of response to your application: the javascript response. Let's replicate the previous example using Remote Form Helpers. (Refer to the plugin's documentation and our chapter on Using and Creating Plugins for more information.)

First, let's replace the linkTo() call from the first example with a call to a function included by the plugin, called remoteLinkTo():

<cfoutput>

<!--- View code --->
<h1></h1>
<p></p>

#remoteLinkTo(text="Alert me!", controller="say", action="hello")#

</cfoutput>

remoteLinkTo() is all you need in your view now, so go ahead and remove the whole script block from the first example. This new function will take care of creating the JavaScript for you.

Now let's review that say controller:

<!--- Controller code --->
<cffunction name="hello">
<!--- Prepare the message for the user --->
<cfset greeting = {}>
<cfset greeting["message"] = "Hi there">
<cfset greeting["time"] = Now()>

<!--- If your request is an AJAX request, respond with the `views/say/hello.js.cfm remote view` --->
<cfif isAjax()>
<cfset renderRemotePage()>
</cfif>

<!--- Otherwise, let Wheels do its normal HTML response (the `views/say/hello.cfm` view file) --->
</cffunction>

Wait, what? hello.js.cfm? Yes, the Remote Form Helpers plugin enables you to use a whole new set of view files. All files that have a .js.cfm extension are considered "remote view files" and will be used with your renderRemotePage() function to send it back to your initial request.

Now let's create the file at views/say/hello.js.cfm and add this code:

<!--- Remote View code --->
<cfoutput>

#pageInsertHTML(selector="h1", content=greeting.message)#
#pageInsertHTML(selector="p", content=greeting.time)#

</cfoutput>

That's it. Without a single line of JavaScript, we were able to setup an AJAX-powered Wheels application.

Provides Plugin

Note that you will also need the jQuery code from the "Do It Yourself" approach above for this example to work.

Provides will allow Wheels to extend the format of a controller's responses to the browser, making it ideal for the first example we discussed near the beginning of this chapter. (Refer to the plugin's documentation and our chapter on Using and Creating Plugins for more information.)

Now let's revisit that code. Everything in our view file will remain the same except for a tiny addition in the linkTo() function:

<cfoutput>

<!--- View code --->
<h1></h1>
<p></p>

#linkTo(text="Alert me!", action="hello", id="alert-button", params="format=json")#

</cfoutput>

Adding format=json to the params argument lets the Provides plugin know what type of response that your application is requesting.

Next, let's revisit our controller code and we make it more robust and extensible:

<!--- Controller code --->
<cffunction name="hello">
<!--- Prepare the message for the user --->
<cfset greeting = {}>
<cfset greeting["message"] = "Hi there">
<cfset greeting["time"] = Now()>

<!--- Provides will determinate the format you want and convert the data appropriately --->
<cfset renderWith(greeting)>
</cffunction>

In this example, renderWith() will see that a format of JSON was requested and format the string into JSON before sending it to the browser.

AJAX Explained

That is it! Hopefully now you have a clearer picture on how to create AJAX-based features for your web applications.

Remember that any of the 3 examples work just fine. You have several ways to accomplish the same goal. Choose the one that fits your style and needs.

^ Top
Table of Contents

Comments

Read and submit questions, clarifications, and corrections about this chapter.

[Add Comment]

  1. Raúl Riera's Gravatar Raúl Riera says:

    Oh man! a BIG GOTCHA for this is to remember to have ColdFusion's debugging turned off, otherwise that debug info creeps into your remote view files and just breaks any Javascript execution in them...

  2. Mike Henke's Gravatar Mike Henke says:

    As Raul mentioned add to your ajax output so debugging is turned off.

  3. Dorin's Gravatar Dorin says:

    Guys, Raul is right. The first example is not working.
    You had to add
         $(document).ready(function(){
    to have things up and running.
    This works in development mode as well.

  4. Jim's Gravatar Jim says:

    Hi guys. I am a new coder to CF and Wheels. I must say I love this framework. Gets even novice developers like myself up and running quickly. Having said that, I am not able to get the Remote Form Helpers plugin to work on my development server. Keeps throwing $INSERTDEFAULTS undefined error. I have debug turned off and I am in the beta of 1.1 wheels. Any thoughts?

  5. Per Djurner's Gravatar Per Djurner says:

    Hi Jim, that plugin has probably not been updated to work on the 1.1 version yet. Hopefully once it's out of beta plugins will start to get updated too.

  6. Jim's Gravatar Jim says:

    Thanks.
    I switched back to the 1.0.5 wheels version and that error went away and the sample file works now. However, I am having trouble getting a remote query to work. I am probably using the wrong function. Using renderJavascript(). I know I can get this to work just using plain jQuery, but would love to be able to use the CFW tools if possible.

  7. Steven Esser's Gravatar Steven Esser says:

    All the examples and methods look nice, but I do think that the first method is one of the better ones. "Why?" you might wonder... well, that method is the only one that will also work in a browser where javascript is turned off. As it uses a normal linkto() element, when ajax won't work with javascript being off, it will still do the request with a normal refresh, while all the other methods shown are specific to Ajax requests. Ofcourse you can use those too, but than you probably will have to test for javascript being active in the browser and then select the appropriate views / interface and it's controllers to use.

    So I hope that when this get's implemented into cfwheels 1.1, that it will be done in such way that we don't have to do endless checking procedures whether the client is using javascript or not.
    I'm developing websites in cfwheels that have to work both for normal PC/TV browsers as well as phones and we cannot expect Javascript to be switched on for that matter.

  8. Andrew Myers's Gravatar Andrew Myers says:

    Another possible "gotcha".  If like me you are following through these tutorials without having set up URL rewriting, you'll need to change this line:

    url: $(this).attr("href") + "?format=json", // References "/say/hello?format=json";

    to

    url: $(this).attr("href") + "?format=json", // References "/index.cfm/say/hello?format=json";

  9. Kirby's Gravatar Kirby says:

    I keep getting an invalid JSON error from the html tags and can't figure out how to get them to stop being added to the hello template.

  10. Charles's Gravatar Charles says:

    Hello,

    I did exactly what was in the example, do not run? Is there any update to the tutorial

  11. Charles Reitz's Gravatar Charles Reitz says:

    One correction for this row

    url: $(this).attr("href") + "?format=json", // References "/say/hello?format=json";

    to

    url: $(this).attr("href") + "&format=json", // References "/say/hello?format=json";

    If you don´t change de "?" from "&" not work

  12. sam's Gravatar sam says:

    just a generic tip for troubleshooting AJAX, try firebug with the console up and the XHR tab selected to see what's really going on between the request and responses. indispensable!

  13. Daniel's Gravatar Daniel says:

    While this does not have that much to do with the Ajax call itself it's, just a note about the security.

    If you're using any sort of security controls on your controller.cfc class, if you overwrite the init function, you remove that functionality. I would suggest to use super.init() to get that functionality(and security) back.

        <cffunction name="init">
            <cfset provides("html,js")>
            <cfset super.init()>
        </cffunction>;

  14. Todd's Gravatar Todd says:

    Is it possible to add an AJAX example that sends a jQuery variable to the controller, the controller does something with that data, and then sends it back to jQuery?

    For example, jQuery gets the id number from a change() event and sends it to an action in the controller which filters a query based off of that id number, and sends the results back to jQuery.

  15. Shari's Gravatar Shari says:

    I am using first example and i am getting parseError can any one please help me out with this

    Thanks in advance

  16. I had Prefix serialized JSON with //. If you stick with CF ajax, then removal of prefix is done automatically. However since this example uses jQuery you might consider either turning that feature off in CF admin for example or adding the following code to remove prefix:


      $.ajaxSetup({
      dataFilter: function(data,type){
       return data.substring(2,data.length);
      }
      });

  17. Paul Rowe's Gravatar Paul Rowe says:

    The code above is using an older version of Remote Form Helpers. The latest version of Remote Form Helpers (12 Dec 2012) does not provide remoteLinkTo or renderRemoteSite anymore.

    Instead of #remoteLinkTo(...)#, use the following:
    #linkTo(text="Alert me!", controller="say", action="hello", remote="true")#

    Instead of renderRemoteSite, use:
    renderWith(greeting)

    Finally, make sure you consult the documentation for Remote Form Helpers to make sure that you have it set up properly.

  18. Jon's Gravatar Jon says:

    I'm getting an error "The method $initControllerClass was not found in component controllers.Say" when trying to use the code from the top example.

  19. Kelly Carter's Gravatar Kelly Carter says:

    I got the same error as Jon (Aug 19, 2013) because I accidentally saved some bogus code in my say.cfc file. I had included some HTML instead of solely the CFML code of the example.

  20. Nathan Stanford's Gravatar Nathan Stanford says:

    Can someone update this code so that it works if you cut and past these items?

    I cannot seem to get it to work... I got the JQuery and Stylesheet to work but can not get it to display the pulled ajax stuff.

  21. Blake's Gravatar Blake says:

    I second Nathans comment. Update this page with more complete coded and instructions need to get this working. I have run smoothly through all the tutorials till I hit this one. Nothing I've tried works. I've followed suggestions on this pages comments section and the community board but nothing has worked.

Add Comment