Getting started with YUI’s Connection Manager in Rails and PHP; or "All Happy Families Are Not Alike"


Javascript/AJAXPHPRuby

This post is geared towards folks who haven’t done the A part of AJAX before (And I mean the first A, as in Asychronous); are new to Yahoo’s implementation of the XMLHttpRequest object (The Yahoo! Connection Manager) and would like added information on how that works; or bothThis is not meant to supplant the excellent yui! tutorials which you should read in detail for thorough explanations and examples. What I am adding here are a few examples of using this in the Rails framework and some thoughts on the callback object and scope.

Your “AJAX” goals are simple: you want to communicate with your server, get a response back that you can use (or not), do something with that response (or not), and move on. As this is asynchronous, you want to do this without reloading your web page. Or, as a client once said to me, referring to certain animated gifs in the upper right-hand corner of certain browsers, without “making the world spin”. To this end, Yahoo! has supplied us one line of code:

var transaction =
YAHOO.util.Connect.asyncRequest(
method, uri, callback, postData);

or, same line with some data plugged in:

var transaction =
YAHOO.util.Connect.asyncRequest(
'POST',   'php/post.php', callback,"id=1&old_id=2");

When I was making the switch from synchronous to a-, it helped me to visualize a standard web form to see how form elements and attributes are translated to an AJAX request. It’s pretty obvious, but if you need an “aha!” moment, the above line is akin to the html form printed below (though unless you’re one of those people whose definition of interactivity is “The Monologue”, do refrain from creating forms with 2 hard-coded hidden inputs and nothing else! :) ).

<form method="post" action="php/post.php">
<input type="hidden" name="id" value="1" />
<input type="hidden" name="old_id" value="2" />
<input type="submit">
</form>

So, excluding a reference to the callback for the moment (which is not addressed in this example), that form maps to the Connection Manager call quite simply: method, action (uri), data. Let’s look at the arguments required:

method: the method of the server request (POST, GET and others also available).

uri: the uri that’s receiving and processing the data you send (in our example, “php/post.php”). YUI’s examples use php, but, if you’re using the Connection Manager in a Rails app, it’s easy to adapt: your argument uri might read “/projects/update which would pass the data to the update method in projects_controller.rb, which would then be able to access the data through the params array, like so:

def update
@project = Project.find(params[:id])
end

In php you’d probably do some type of db query [assume input cleanup and some type of database abstraction layer, such as PEAR/DB_DataObject, here]

$project = DB_DataObject::factory('Projects');
$project->get($_POST['id']);

Callback: a reference to the callback object you are supplying. This is how everything is handled. More on that in a minute.

postData: the data itself in standard query-string format (“new=1&old=2″). NOTE: if you’re doing a GET transaction, your 4th argument would be false and your second argument would include the url and query string, like so:

“php/post.php?new=1&old=2".

So, what’s this callback?. In a synchronous transaction, you have the luxury of redrawing the page to process your data (and yes, nothing says luxury like a nice, slow, page reload…). In an asynchronous transaction you need to essentially “sneak” your data back into the page without reloading it. This is where your callback object comes in. It helps you get your data “in the door”, so to speak, so your page or application can change in a way that feels seamless to the user but often returns a visible result (changing a div, displaying some text, etc.) and if not a visible result at least a meaningful one (setting the value of a hidden form element, for example). Your callback is responsible for executing actions based on the data retrieved (or the failure to retrieve data) from the uri. In a standard synchronous form this action might be “generate an HTML table that displays your database results”. Or, “Print a message saying there are no results”. Of course, you can do anything you want with your data, that’s just an example of a fairly common scenario.

Once you’ve sent your data to the uri for processing, you need to wait for your response — without, of course, appearing to wait (save for the ubiquitous web 2.0 spinner you know you’re dying to try!). And, of course you want to know if your transaction failed. If you don’t watch for these things — “success” and “failure” in technical terms — you’re not going to be able to make an appropriate decision about what to do next in your app. So you feed your AJAX request a callback object: an object that defines functions for what to do in the cases of success and failure. In simplest terms, we’ve got

var callback = {
success: handleSuccess,
failure: handleFailure
};

where “handleSuccess” and “handleFailure” are user-defined functions that take the http response object and do stuff with it.

handleSuccess = function(o){
// cheer! (or process data returned from the server)
}

handleFailure = function(o){
// cry, vow to try again! (or display failure message)
}

There’s also the ability to pass scope, timeouts, and additional arguments to the callback object. To do so you’d read the great tutorials at the links above and add the lines below, of course changing the values to values meaningful in your application.

scope: Ajaxobject,
timeout: 5000,
args: ['arg1', 'arg2']

The handlers. handleSuccess and handleFailure both take an object o, which is the http response object. There’s a detailed list of all the properties of o on the yui page (Not to be confused with the Story of O, which I will not link to as it is beyond the scope of this article, you dirty rascals, you…). The property you’ll likely use most often is o.responseText, which is the server’s response as a string. This is what you pass back from good old ‘php/post.php’, and getting it is simple: echo. What? echo. What? echo..o..o… ok, sorry, moving on. For instance, if we wanted to capture the update_date in our successHandler to print to our page and we’re using php, we’d write something like this:

echo $project->update_date;

and if we’re in Rails? something like this:

render_text @project.update_date

If you need more data than a string — an array or collection of objects passed back from the server, you’ll find that’s simple, too: call the ruby method to_json() on your array instead. This essentially serializes your object so it can survive the journey to the Client. Once there, you can access the data using JavaScript’s magic wand: eval(). It’s great. So if you had an array of users connected to a project (and your database relationships are set up correctly), you could write

render_text @project.users.to_json

in php, assume you’ve got your $users array, and use print_r

print_r($users);

The in your JavaScript successHandler use eval(), like so:

var users =  eval(o.responseText)

and bingo: in two lines you’re happily in your JavaScript parsing your users array like you would any other JavaScript array. You have connected your server to your DOM and no one is the wiser for it.

All this is great. We have our AJAX call and our callback object and are ready to go. But, suppose you don’t want to rewrite the AJAX call all over your app? The Yahoo folks have a great example of a ‘“hypothetical ajax object” (mysteriously named “AjaxObject”) that encapsulates success, failure, and process methods and calls a callback object that defines AjaxObject as it’s scope. Encapsulating your AJAX request so you can call it from wherever you want in your scripts in a DRY fashion makes your code cleaner and easier to manage. Yahoo! does this well in their example: in my usage I changed it up a little bit to meet my needs.

To quote the great Chicago writer Leo Tolstoy from his famous novel Anna Karenina Does Lake Michigan, ‘“Happy families are all alike; every unhappy family is unhappy in its own way”. I’ve learned that when working on an app, the opposite is true: success cases call for a range of actions: failures can more easily handled (log, display error, abort). Based on this, I’ve adapted the yui AjaxObject example to accept a postAction, successHandler, and object (used to define scope). This allows you to call AjaxObject from other objects, pass specific success handlers, and pass this (a reference to your current object) so you can access it in your successHandler from within the calling object. The AjaxObject builds the callback using those arguments. Like so:

var AjaxObject = {

handleFailure:function(o){
// Fail gracefully
},

/**
* Wrapper for AJAX calls using YUI connector
*
* @param postAction {String} URL to post to
* @param callBackSuccess {String} Success handler
* @param postData {String} Data to post
* @param obj {Object} Object that handler has scope in
*
*/
startRequest:function(postAction, callBackSuccess, postData, obj) {

var callback = {
success:callBackSuccess,
failure:this.handleFailure,
scope:obj
}
// ASSUME you've shortened your yui connection mgr to $C
$C('POST', postAction, callback, postData);
}

};

Then you can call AjaxObject from within a class, like so, and pass it a class method as it’s success-handler:

var Project = function Project(){
// initialize project however you like
this.foo = "bar";
...
// CREATE in db and return id
AjaxObject.startRequest('/project/create',
this._generateDbId, postData, this);

}   // Success handler  Project.prototype._generateDbId = function(o){
if(o.responseText !== undefined){
this._setDbid(Number(o.responseText));
// DO other stuff..
}
}

This way your AJAX calls are in one place, you can use them in the scope of the calling object and define as many success handlers as success cases (or pass false); and fail in one standard way (gracefully, of course). Of course, this can be adapted to pass failure cases in too, or however you like. This was a way I found helpful in my work, and I hope it’s helpful to you as well. And thanks again to the folks at Yahoo! for providing so much great stuff to work with in the first place.


8 Responses to “Getting started with YUI’s Connection Manager in Rails and PHP; or "All Happy Families Are Not Alike"”

  1. Shishir

    I am using YUI file uploader with connection manager.I need to check the content of the server
    response but i am unable to get the response because javascript always says the response is
    undefined.I tested with FireBug which shows the correct response from the server.
    Where is the problem? Is it parse error or something else?

    Reply
  2. Brian

    I am hoping that you can help me with this. I have just started building websites a month ago. I have many forms where I want submit data to data to mysql database. I am calling a php from an html form and then doing a page reload. I realize this is not very efficient and using the YUI interface above would help. My problem is that I don’t know how to call the call the remote call from my form. Can you help me? Thanks so much. Any advice in this area would be appreciated.

    Reply
  3. sarah g

    Hey @Brian,

    It sounds like you’re talking about the difference between a regular form and one submitted remotely, via XHR. Really, one isn’t more efficient than the other, they just serve different needs. For instance, an AJAX call in a form would be helpful if you wanted to pre-validate a field: maybe when the user fills in their email address, you want to check behind the scenes to see if it’s already taken so you can give feedback right away and not wait till the form is submit. But, if you’re simply submitting information that has no need for pre-validated feedback, I don’t see a need for Ajax, and in fact, using it in those circumstances can be a pain in the ass for the user.

    That said, the way to make a remote call from a form, and this is broad-overview here, is via JavaScript callbacks. By attaching actions to different JavaScript events, you then have an opportunity to make a remote call when the action executes.

    So, the first thing to study up on is how to attach listeners to events. Turns out I wrote another post on this blog you can reference for that, though it’s totally tip of the iceberg: http://www.devchix.com/2007/02/13/a-brief-look-at-yahoo-event-utility-addlistener/

    In essence, read up on event listening with JavaScript (look at YUI, or Prototype and LowPro, or JQuery) so you get a feel for that. And at that point you can think about using it to make remote calls… if there’s a good user-backed reason to do so.

    Hope that helps!

    Reply

Leave a Reply