You are viewing documentation for v1.1.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 built-in 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()):

(function($){$(document).ready(function(){

    // 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") + "?format=json", // References "/say/hello?format=json"
     dataType: "json",
     success: function(response) {
     $("h1").html(response.message);
     $("p").html(response.time);
     }
     });
     return false; // keeps the normal request from firing
    });

});})(jQuery);

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. Additionally, the JavaScript call is passing a URL parameter called format set to json.

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 provides() and renderWith() functions:

<!--- Controller code --->
<cffunction name="init">
    <cfset provides("html,json")>
</cffunction>

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

<!--- Respond to all requests with `renderWith()` --->
<cfset renderWith(greeting)>
</cffunction>

In this controller's init() method, we use the provides() function to indicate that we want all actions in the controller to be able to respond with the data in HTML or JSON formats. Note that the client calling the action can request the type by passing a URL parameter named format or by sending the format in the request header.

The call to renderWith() in the hello action takes care of the translation to the requested format. Our JavaScript is requesting JSON, so Wheels will format the greeting struct as JSON automatically and send it back to the client. If the client requested HTML or the default of none, Wheels will process and serve the view template at views/say/hello.cfm. For more information about provides() and renderWith(), reference the chapter on Responding with Multiple Formats.

Lastly, 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 instead), 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. jQuery will parse the JSON object and populate the h1 and p with the appropriate data.

Approach #2: Remote Form Helpers Plugin

The 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.

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.

AJAX in Wheels 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

Add Comment