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

Event Driven Programming : Server Communication

We've been examining the specifics of how to create event driven programs for the last several blog posts. But somehow, we need to integrate jQuery custom events with our server code. In this post, we'll see how we use Ajax to do just that.

In the last post on identifying events, I said that a good event name needs to have both a group and an action -- something like Cart:Add, perhaps -- and that, further, the groups we choose will be informed by the controllers we intend to create on the server. (Cart:Add indicates we probably have a controller named "Cart".)

How do we get from the world of jQuery custom events to the server side? We can do it the traditional way -- an HTTP request through the browser, of course, but my preferred way of doing this is by passing data through the "tunnel" of the XHTTP Request object. Sounds intimidating. Luckily, jQuery makes it very easy with jQuery.get and a jQuery.post methods. (Throughout the code, I'll be using the built-in $ symbol which is an alias to the jQuery object.)

Let's start with an example I've created. Here is a form for adding a new customer:

And the code producing that form:

customer_form.html
<html>
  <head>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
  </head>
  <body>
    <div id="notice"></div>
    <form id="customer_form" method="post" action="Customer.cfc?method=create">
      <label for="customer_name">Name</label><br />
      <input type="text" name="customer_name" id="customer_name" />
      <br />
      <label for="customer_email">Email</label><br />
      <input type="text" name="customer_email" id="customer_email" />
      <br />
      <label for="customer_refer">How did you hear about us?</label><br />
      <input type="radio" name="customer_refer" value="search" />Search engine<br />
      <input type="radio" name="customer_refer" value="friend" />Friend<br />
      <input type="radio" name="customer_refer" value="luck" />Dumb luck<br />
      <br />
      <input type="submit" value="Add Customer" />
    </form>
    <div id="help"></div>
  </body>
</html>

As you can see, apart from the call to load the jQuery library in the head of the document, there's no jQuery or JavaScript on the page. Why? One of the best ways to write client-side code is by using something called unobtrusive JavaScript -- meaning that the code should work, even if there is no JavaScript. That's the case here: we have a perfectly normal form.

But since we do want to use jQuery's Ajax capabilities, we'll include another file, customer_form.js.

customer_form.js
$( '#customer_form' ).submit(function( e ){
  $( '#customer_form' ).trigger( 'create_customer' , event( 'create_customer', formToObject( this ) ) );
  e.preventDefault();
});

This code intercepts the normal form submission and, instead, announces a jQuery custom event, create_customer. In the process of doing this, it calls two functions, event and formToObject. The event function returns a simple event object structure.

Is there some standard?

Now, perhaps you're wondering, "Is there some standard for event objects?"

Can he not hear me?

No. Now, the great strength of event driven programming is that the components that subscribe to events and those that listen for events aren't aware of each other. This independence is key to maintainability of code. But having a standardized event object provides a mechanism for listeners and publishers to communicate -- even without knowing about the other.

This should be fun

Here is a simplified version of an event object that I'll use. This example shows an event for creating a customer:

{
  name : 'create_customer',
  id : '01CC99F7-77F3-4815-8BBB-1FF43D3AC3D3',
  time : new Date(),
  payload : {
    name : 'Hal Helms',
    refer : 'friend',
    email : 'hal@halhelms.com'
  }
}

The payload represents concrete information about the event; the other attributes are metadata about the event.

So, the event function returns an event object in this structure. It accepts two arguments: an event name (required) and a payload (optional).

The other function, formToObject takes a form and turns the fields (and their values) into a JavaScript hash -- perfectly suited to be a payload, by the way. Because these functions will be used so often, I'll place them into a helpers.js file, where I'll put other useful, general scripts. Then, that JavaScript file will be included at the bottom of the customer form HTML page.

Now, let's see the form in action:

Sniff...what a beautiful ending.

At this point, we've intercepted the normal form submission, we've created a hash representing the event object, and we've announced a custom event, create_customer. From the form's point of view, it's done its job. Now, we need to get some help from listeners. Here is the CustomerListener -- listening for all events related to customers.

CustomerListener.js
$( 'body' ).bind(  'create_customer', function( event, event_object ) {
  $.get(
    'Customer.cfc?method=create',
    {"event_object" : JSON.stringify( event_object )},
    function( response ){
      $( 'body' ).trigger(
        response.name,
        response
      );        
    },
    'json'
  );
});

Remember that we use jQuery's bind method to listen for events -- and here we're listening for a create_customer event. When this code is notified that such an event occurred, it will use jQuery's get method to communicate with the server, asking it to create a customer and passing (via the event object) everything it needs. I've also specified a function that will be executed when we get a response back from the server. We'll talk more about this shortly. For now, let's look at the code on the server. I'm using a ColdFusion component, but this will work with any server technology that can return a JSON string.

Customer.cfc
<cfcomponent displayname="Customer" output="false" hint="I am a Customer controller">
  <cffunction 
    name="create" 
    access="remote" 
    output="false" 
    returnformat="json" 
    hint="I create a new customer">
    
    <cfset args = DeserializeJSON( arguments.event_object )>
    
    <!--- Code for creating object, persisting to the DB, etc would go here.
    For now, I'm simulating getting back a customer ID of 1001 --->
    
    <cfreturn {
      'name' = "customer_created",
      'id' = CreateUUID(),
      'payload' = {
        'customer_id' = 1001,
        'customer_name' = args.payload.customer_name
      }
    }>
  </cffunction>
</cfcomponent>

Notice that after the customer creation area, I've returned a new event object. Now, if we go back to the CustomerListener, we can look at the function that will be called when the server responds:

function( response ){
  $( 'body' ).trigger(
    response.name,
    response
  );        
}

The function announces a new event called "customer_created", based on the event object returned by the server.

But, just as you finish up this code, your boss appears.

Why do I speak in questions all the time?

You're just about to change the function when you realize:

I can use a messaging listener!

And while your boss is still there, you whip out this code:

MessagingListener.js
$( 'body' ).bind(
  'customer_created',
  function( event, event_object ) {
    $( '#notice' )
      .html( 'The customer was created and has a customer id of ' + event_object.payload.customer_id )
      .fadeOut( 7000 );
  }
);

A message listener. Slam bamma ramma!

Of course, your boss has strolled off by this point. Looking at you code was so mesmerizing he forgot what he was waiting for. Almost time to call it a day, but...hey, it's that cute girl in customer service.

I *totally* forgot about a help system!

This is the sort of thing that, in the past, would have sent you into a fantasy world of reprisals for people who, after the project is done, slip in "little" requests. But, in a fit of precognition, you did put in that "help" div in the original markup. Hmmm...would it really be so hard?

HelpListener.js
var messages = {
  customer_created : "After creating a customer, remember to send them an email asking them to register for the company competitive sushi-eating team!"
};

$( 'body' ).bind( 'customer_created', function( event, event_object ) { console.log( event ); $( '#help' ).hide().html ( messages[ event_object.name ] ).fadeIn( 3000 ); });

Uh oh. Here's your boss again, but -- he's smiling!

Good work!

A bonus? And the cute girl thinks you're great? Hmmm...this event driven programming stuff may be worth the effort.

We've got the start of a pretty good system here, but there are still some things that need refactoring. Why don't you try to identify the areas where the system needs work.

In the meantime, you can find the sample code for this blog post at http://github.com/halhelms/Blog-Post---Event-Driven-Programming---Server-Communication

[Oh, yeah -- and you remember the Boss said he thought somebody was going to get a bonus. Sadly, I think he was talking about himself...]

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Ben Nadel's Gravatar Good write up. I'm still letting all of this stuff saturate my mind. I have a long history with direct invocation and I'm having trouble embracing the idea that stuff "just happens."
# Posted By Ben Nadel | 10/27/10 11:11 PM
Hal Helms's Gravatar I know what you mean, Ben. I went through the same thing. In my experience, direct invocation works -- right up to the point where it doesn't!
# Posted By Hal Helms | 10/27/10 11:13 PM
Ben Nadel's Gravatar @Hal, I just looked into node.js for the first time over the weekend. It's basically a server-side Javacript environment. One of the core philosophies of the node.js server is that ALL I/O operations are non-blocking (ie. asynchronous). Definitely, when that is a founding principle, it changes the way you think about everything.

I think one of my concerns is that I would have something that was direct-invocation and switch to async pub/sub invocation and suddenly my code would break. But, if you embrace the fact that all I/O (whether its AJAX, local database, etc.) is asynch, then I think building pub/sub will be easier.
# Posted By Ben Nadel | 10/27/10 11:30 PM
Hal Helms's Gravatar I think node.js -- or one of its successors -- is going to be very important, Ben. When you think about it, asynchronicity is the way the world primarily works. The direct invocation model is one forced by older computer technology to the point where it *feels* natural when it's anything but.
# Posted By Hal Helms | 10/27/10 11:45 PM
Ben Nadel's Gravatar @Hal, That's a good point. And it's not just this kind of stuff either. Look how much traction NoSQL databases are getting - apparently the "relational" model is the "one" true way either.
# Posted By Ben Nadel | 10/27/10 11:57 PM
Drew's Gravatar Node.js is an interesting concept, I hope they do work to make it OS agnostic. Many things that are linux only fail to get any traction in the community.
# Posted By Drew | 10/28/10 11:41 PM
 
   
Clicky Web Analytics