Halhelms
SIGN UP FOR MY NEWSLETTER

www.savorgold.com is top on wow gold and runescape gold and World of Warcraft gold provider list for trusted services. Their reputation seems to growing by the minute, which isn't surprising because they are one of the safest sellers of Gold. Delivery speed and customer service are very good. They aslo are giving some bonus items depending on the amount of gold you purchase.

 
 
Halhelms

Recent Comments

Recent Entries

RSS

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]

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Raymond Camden's Gravatar A bit OT:

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.
# Posted By Raymond Camden | 10/14/10 5:33 PM
Raymond Camden's Gravatar Hal, I apologize. You were not returning a JSON string - I just misread the struct shorthand notation as JSON itself.

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.
# Posted By Raymond Camden | 10/14/10 5:34 PM
Raymond Camden's Gravatar Ok, so after nitpicking, my questions:

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! :)
# Posted By Raymond Camden | 10/14/10 5:40 PM
Hal Helms's Gravatar @Ray Now, I didn't know that about adding the "returnFormat=JSON" in the request. Nice! And, I agree, it's much better.

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!
# Posted By Hal Helms | 10/14/10 6:09 PM
Tony Nelson's Gravatar @Ray,

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.
# Posted By Tony Nelson | 10/14/10 6:29 PM
Tony Nelson's Gravatar @Hal (& Ray),

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();
});
# Posted By Tony Nelson | 10/14/10 6:37 PM
Hal Helms's Gravatar You'd need to move init to a public function for that to work. I certainly see nothing wrong with that; do you see an advantage that I missed?
# Posted By Hal Helms | 10/14/10 6:41 PM
Tony Nelson's Gravatar Not really, other than it just looks more familiar to me. :)

I guess since your skeleton "class" doesn't have any public methods, then there's really no point in giving it a name.
# Posted By Tony Nelson | 10/14/10 6:46 PM
Hal Helms's Gravatar @Tony. In this case, no name, but often I do give it one so that other code can interact with it.
# Posted By Hal Helms | 10/14/10 7:11 PM
 
   
Clicky Web Analytics