Mail Call : jQuery Custom Events, Part 2
In yesterday's blog post, I began answering an email about jQuery custom events. Today, I'll show code that uses jQuery custom events to deal with the fact that Ajax calls are asynchronous.
Back to my response to the email:
So, there's the why. Now for the how.
You already saw how I use a JavaScript skeleton. [Shown here] I'm going to integrate jQuery custom events with this skeleton in order to deal with asynchronous Ajax calls. First, though, take a look at the controller -- the code that will respond to the Ajax request. Here is some sample code in ColdFusion:
<cfcomponent displayname="Customer" output="false" hint="I act as a Controller for this example."><cffunction name="create" access="remote" output="false" returnformat="json" hint="I manage the creation of a customer. For this example, I'm just simulating it."> <cfargument name="customer_name"> <cfreturn { 'success' : true, 'customer_id' : '1007', 'customer_name' : arguments.customer_name }>
</cffunction>
</cfcomponent>
This sample code doesn't actually do any saving to the database, etc. That, I'm sure you know how to do. But notice the structure being returned.
There's nothing fixed about this structure, although I typically do have a uniform response that includes keys for success, data, and a few other things. As I said earlier, I'm showing a simplified version of this.
OK, so seeing how the CFC responds to the Ajax request, let's get to the page that does the requesting. Here's the start of the code:
<html>
<body>
<div id="main">
<form id="new_customer">
<label for="customer_name">Customer
<input type="text" name="customer" id="customer">
<br />
<input type="submit" value="Add Customer" />
</form>
<div id="customers"></div>
</div>
...
Just a standard, HTML form -- nothing JavaScripty or Ajaxy about it. We're about to make it so by introducing unobtrusive JavaScript -- code that will run if the user has JavaScript enabled (which according to a recent tweet report of a study, 98% of users do).
Here's the JavaScript code:
<script
type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js">
</script>
<script type="text/javascript">
( function() {
// private variables *******
// private functions *******
// respond when a customer is added
function bind_customer_added() {
$( '#main' ).bind(
'customer_added',
function( event, event_object ){
$( '#customers' ).html(
'Customer ' + event_object.customer_name +
' with an ID of ' + event_object.customer_id +
' was added to the system.' );
// I'll explain this shortly...
//$( '#main' ).attr( 'id', 'vermon' );
}
);
}; // end bind_customer_added
// DOM handlers
// trap the form submission and process it
function handle_new_customer() {
$( '#new_customer' ).submit( function() {
$.post(
'Customer.cfc',
{
method : 'create',
customer_name : $( '#customer' ).val()
},
function( response ){
if ( response.success ){
$( '#main' ).trigger(
'customer_added',
response
);
};
},
'json'
);
return false; // prevent the form from submitting normally
});
}; // end handle_new_customer
// initialization
( function() {
bind_customer_added();
handle_new_customer();
}
)();// public API
return {}
})();
</script>
Shall we go through that code?
You'll recognize that I start with my JavaScript skeleton.
( function() {
// private variables *******
// private functions *******
// initialization
( function() {
}
)();// public API
return {
}
})();
Let's skip over the private methods section for a moment. (We'll come back to that). Right now, concentrate on the code in the initialization section.
In the original post on my skeleton, I had named the initialization method as "init". Nothing special about that name, btw, but having some name is useful if you think you might need to recursively call the init file. Madness, you say? Well, check out Ben Nadel's excellent post on this topic: http://www.bennadel.com/blog/1988-Using-Named-Functions-Within-Self-Executing-Function-Blocks-In-Javascript.htm.
Anyway, here I didn't bother naming it as I won't be making any recursive calls. Now, within the initialization code are calls to two functions. At the very end of my response, you'll see why I did this. So push that onto your stack.
I also introduced a new section to the skeleton: DOM handlers. I make a distinction between code that deals with typical DOM events -- things like click, blur, etc. -- and code that deals with custom jQuery events. Both types of code use the jQuery "bind" method, but here's the distinction: DOM handling code -- something like this:
$( '#more_info_button' ).click( function() {
// do something
});
What's that? I said that both this code and jQuery custom events use the "bind" method -- but you don't see a "bind"? That's because the code shown is just a shortcut for this:
$( '#more_info_button' ).bind('click', function() {
// do something
});
Anyway, this is what I call a DOM handler. DOM-handling code is meant to respond to DOM events. But what does it do? Remember, this is a simplified example and the code here is somewhat simplified, but instructive, nonetheless:
function handle_new_customer() {
$( '#new_customer' ).submit( function() {
$.post(
'Customer.cfc',
{
method : 'create',
customer_name : $( '#customer' ).val()
},
function( response ){
if ( response.success ){
$( '#main' ).trigger(
'customer_added',
response
);
};
},
'json'
);
return false; // prevent the form from submitting normally
});
}; // end handle_new_customer
The code intercepts the plain HTML form code (shown in the first code listing). It then binds the form's "submit" event in such a way that the form is never actually submitted. Instead, I've taken the form value I need (only one in this example) and sent it along via Ajax to the controller CFC I showed you above. So far, no custom events, but that's about to change.
The jQuery "ajax" method (and its "get" and "post" shortcuts) can accept a function that will run on the successful completion of the XHTTP request. (By success, I'm not implying that the work done by the controller in responding to this XHTTP request was successful. It's just that the request was received and a response was sent. In a moment, we'll test to see if the server not only responded, but was able to process my request.)
That function...
function( response ){
if ( response.success ){
$( '#main' ).trigger(
'customer_added',
response
);
};
}
... uses a conditional statement to see if "response.success" is true. Who determined whether the response is successful? Our controller did. If, for example, the database insertion of the new customer had failed, then our controller code would have set response.success to false.
Anyway, we got a successful response from the server. Now what to do? Announce a custom event -- an event that is meaningful to us. That's what the "trigger" code is doing, announcing a custom event called "customer_added" and sending the same response that it got along with the custom event.
So, we've satisfied that "separation of concerns" thing. The DOM handling code was responsible for two things:
1. asking the server to add a customer and
2. announcing that a customer had been added
Who's listening for that "customer_added" event? Not that function's problem to worry about. It's done its job.
Of course, someone is listening -- and to see that, let's look at the private function that I promised we would get to. First, though, one clarification: because an event is announced, there's no guarantee that anyone is listening for it. That's fine -- and it will prove to be extremely useful for future updates to the code where a new module may very well need to respond to that event.
The private functions
// private functions *******
// respond when a customer is added
function bind_customer_added() {
$( '#main' ).bind(
'customer_added',
function( event, event_object ){
$( '#customers' ).html( 'Customer ' + event_object.customer_name +
' with an ID of ' + event_object.customer_id +
' was added to the system.' );
// still ignoring this for now...
// $( '#main' ).attr( 'id', 'vermon' );
}
);
}; // end bind_customer_added
Within the function, "bind_customer_added", I have called jQuery's "bind" method. This time, I'm not listening for a DOM event like "click" or "focus" but for a custom event -- in this case, the custom event with the name of "customer_added".
And that's the "event lifecycle", if you will. An event gets announced, someone (may or may not) listen for it, then responds.
Now, in a full-blown event-driven app, the primary communication medium is by events. Lots and lots of events. Many of them go ignored. But the system generates these events just the same. When I was beginning in programming, I thought the Holy Grail was code reuse. (What? I can use the same form to both add and edit the same thing? Kewl!!) I now view code reuse as largely unimportant. Code maintenance, on the other hand, really is important. And what I've found is that using events to unlink the dependency of components is a fantastic way to make code maintainable.
Two other things in this very long response to you: The code with the "still ignoring this for now" comment. If you actually run the code, you'll see that it works continuously: you can add lots of customers. That's just what we want, but I put this little code in here, so that you can see that by removing the element to which the code is bound (or renaming it, in this case), the event handling code associated with this DOM goes away. That may mean nothing to you right now. That's fine. Just keep it tucked in the back of your head and one day, when you're comfortable building single-page interfaces, you may find a need for this. Enough said about that.
The other thing that might give you some pause is this: why did I bother wrapping the jQuery code in separate functions? (Look back at the code and you can see "bind_customer_added" and "handle_new_customer" functions.) We could strip off this outer layer and just let the jQuery code run free.
I used to do this -- and there's nothing wrong with that approach. But by wrapping the jQuery code in a named function, I can call them in the order I wish to. That's sometimes very nice to be able to do -- especially in the initialization code.
I trust that has answered every question you have. Ha! But, hopefully, it will inspire you to think about this stuff. Writing strong, robust apps is hard work. Our brains were not <cfif reader_address.state EQ "Kansas"> designed <cfelse> evolved</cfif> for this. Despite vendors' claims of how simple and easy life will be if we but buy their products, we know otherwise. If your goal is to get rich, sell false hope. If your goal is to produce something of value, study - humbly.
H
[The code for this can be found at http://github.com/halhelms/Blog-Post-Code]


You should not - normally (nothing is 100%!) specify returnFormat in your CFC method. Your CFC should just return the data in a native format, an array, a query, whatever. Don't bother returning it as JSON.
When your remote call passes returnFormat=JSON in the URL (or Form) then CF will automatically format the result for you.
This gives you the benefit of a CFC method that is agnostic about it's return, and also prevents you probably typo'ing the result.
Also - I'm surprised if your code works right at all. You returned a JSON string, but also told CF to auto JSON the result. It probably "worked" but I bet quotes were doubled.
I stand by my earlier comment though about leaving returnFormat off of the CFC method itself. Let the caller decide if he wants native CF or JSON (or, lord forbid, WDDX) back.
1) I lie - one more nit pick. You probably should not use the return false in your form handler: http://fuelyourcoding.com/jquery-events-stop-misus...
2) And now for the real question... I'm still a bit hazy as to your use of an object to handle the page as opposed to the "typical" jQuery usage where all of your code would be within $(document).ready(). Can you address that again - or even perhaps call out where in your entry you explain this (since obviously I didn't quite get it! :)
Excellent link about preventDefault. I thought "return false" was just a shorthand for that. Didn't realize it did too much more.
On the use of an object v. more typical jQuery usage, I'll address that separately.
Thanks for the excellent feedback. I write a blog post -- and *I* get smarter!
If you look at his full example, you'll see that his JavaScript is rendered after his HTML, so there's no need to wait for the document to load -- the self-executing function takes care of itself.
This might be explained in a future post, but is there any big benefit between your current example, and tweaking the anonymous self-executing function to towards some more like:
$(document.ready(function(){
Customer.init();
});
I guess since your skeleton "class" doesn't have any public methods, then there's really no point in giving it a name.