Traffic JS, An Event Driven Approach to Ajax Communications

Clean separation of configuration, event binding and server communication.

Traffic JS implements an object oriented approach to asynchronous http requests. Leveraging and enhancing Backbone.js to augment the Model persistence.

To introduce Traffic let's start with an example. We need to send a request with an id of 123, we need to respond to two listeners, one to update a model and another to display a success message.

//configuration var request = new Traffic.HttpRequest("/somewhere.php"); //event binding request.on("success", updateMessage); request.on("success", updateModel); //sending the request. request.send({ id : 123 });

And again using a typical function based ajax call.

stdAjax({ url : "/somewhere.php", data : { id : 123 }. success : function(xhr){ updateMessage(xhr); updateModel(xhr); } });

The first issue with a function ajax call is that there can only be one success and failure event handler and it needs to be in scope.

The second issue is that it's being immediately configured and sent in the same line of execution. With Traffic a request can be configured during intialization and then reused through the lifecycle of the applications runtime. This way the configuration is preserved, such as URL, Http headers etc. More importantly is that the event listeners only need to be bound once, instead of each time the call to the server is made.

Easy Listening with Events

Event binding with the function ajax call needs to be sent through in the configuration object. This becomes a problem if the listener is an instance method of another object.

With TrafficJS a typical pattern is a controller creates the request and views. Then it binds all of the events between the components. This pattern decouples the request and view into a way such that it's only interaction or reference is the event binding.

function Control(config){ var self = this; this.request = new Traffic.HttpRequest(config.url); this.view = new Traffic.View({ template : config.template }); this.request.on("success", handleSuccess); function handleSuccess(response){ self.view.render() } }

The above example could be handled even better through Models, but for the sake of keeping it simple I've stuck with the basics.

BackboneRequest and Models

Traffic is integrated with BackboneJS and is designed to support the Backbone.sync method and concept.

Instead of hijacking and effectively overwriting the Backbone.sync method, Traffic JS extends both Backbone.Model and Backbone.Collection and overrides the classes sync method and delegates it to Traffic.sync.

var request = new Traffic.HttpRequest("/somewhere.api"); var collection = new Traffic.Collection([], { request : request }); collection.fetch();

The collection will clone it's request object and pass that along to the model. That way it inherits the configuration, but not the event listeners.

Backbone has it's own request class within the Traffic JS library that extends the core HttpRequest object to implement a convienent HTTP Method mapping from Backbone's own vocabulary of verbs to the typical http methods.

var request = new Traffic.BackboneRequest("/somewhere.api"); var model = new Traffic.Model(null, { request : request }); model.set("title", "Example"); model.save();

Down the stack on model.save() will be a call to setMethod("create") which will map to setHttpMethod("POST").

Let's take a look at how we can work the request object in front of the model to achieve a pessimistic architecture.

var request = new Traffic.HttpRequest("/somewhere.api"); var model = new Backbone.Model(); request.on("success", function(response){ model.set(response); });

And once again with an optimistic update with a leery failure handler.

var request = new Traffic.BackboneRequest("/somewhere.api"); var model = new Traffic.Model(null, { request : request }); model.save() .otherwise(function(response){ model.trigger("invalid", model, response); });