Skip to main content

Client side MVC: Using Publisher - Subscriber event model to build decoupled components

I've been really busy these days writing client side MVC code. A few months back I explored using Require and Backbone to build the frontend MVC structure. Since then I've been working on improving the structure of my client side code. One approach that I've used is to decouple the components. When I say decouple, the components that I build should not directly alter or affect the behavior of some other component in the page. By decoupling the components, we can ensure that these set of components can be reused and tested in isolation.

Usually it gets way to easy to get digressed while developing a website without following any code structure. You would end up writing tons of callback functions and it would become hard to maintain that code. Applying MVC structure to your client side code would ensure that the concerns are separated and if there is a bug in one of the components it would be easy to find and fix it. As mentioned in my earlier post, Backbone.js provides set of components that can be extended to build Models, Collections and Views. A Model or Collection can be considered as a Data Access Layer, which communicates with your server and maintains the data structure. The responsibility of the View would be to listen to the changes in the Model\Collection and render the page accordingly. On a page you would find components like the header, navbar, footer and various other pieces that make up the body of the page. Each of these components on the page can be considered as widgets on the page. This is how each widget would be structured:

widgets
    - widget_name
         - build
         - collections
         - models
         - templates
         - views

The widgets directory would contain definitions for various widgets. Each of these widgets contain the following directories:

  • Models - a set of Backbone models, here the data structure in each of the model definition is of a simple object containing few keys.
  • Collections - a set of Backbone collections. Collections would contain an array of objects.
  • Views - a set of Backbone views. These views would have a dependency on the collections, models and templates. It's sole responsibility is to listen to the changes in the Collections or Models and render the page accordingly.
  • Templates - a set client side templates. I have used Underscore.js templates since Backbone already has a dependency on Underscore.
  • Build - the build directory would contain the optimized code (created using Require.js optimizer - r.js).
Once you build these widgets, there would be many instances where the state of one widget would alter the state of another widget. For example, consider that you are building a 'Cart' and a 'Wishlist' widget. In a scenario where you want move an item from Cart to Wishlist or vice versa, these entities should have each other's reference to complete this action. Instead of these widgets storing a reference of another widget on the page, a central entity - 'Controller' would assist in sending messages between these widgets. The Controller would provide a Publisher - Subscriber event model, using which these widgets would publish and subscribe to events:


The Controller uses a 'events' object to store the list of events and its subscribers and it provides methods - 'subscribe' and 'publish' as an interface to other widgets:


The above code snippet defines a module which contains definitions for 'subscribe' and 'publish' methods. The widgets declare a dependency on the 'Controller' and would publish events by calling the publish method:

Controller.publish('movetowishlist', cartObject);

and subscribe to events using

Controller.subscribe(view, 'movetowishlist');

The 'subscribe' method first checks whether the specified 'eventname' already exist. If not, then it creates an entry in the events object. The value of this object is an array of subscribers. Here you also check whether the event was already published. If it has been published then the subscriber would get the published data as soon as it subscribes for the event. This is required when you have multiple widgets on the same page and these widgets have subscribed to the 'loadComplete' event of other widgets on the page. These widgets are loaded asynchronously and when the second widget loads it would not receive the 'loadComplete' event of the first widget. Therefore, we store the data published by widgets and provide it other widgets when they subscribe to it.

The subscriber's view object needs to implement a custom event (in this case 'movetowishlist'), so that when the event is published, the Controller would invoke this event on the subscriber's object. The publish method checks if the specified event already exists in the 'events' object and then iterates over the list of subscribers and publishes the event on the subscribers object. It then stores the published data in the 'publishedEvents' object, this is required for 'loadComplete' events.

The publisher - subscriber event model helps in decoupling the widgets on the page, allowing you to reuse them wherever required. The Controller here acts as a mediator between different widgets on the page and provides a communication channel for these widgets to interact with each other.

Comments

Popular posts from this blog

File upload and Progress events with HTML5 XmlHttpRequest Level 2

The XmlHttpRequest Level 2 specification adds several enhancements to the XmlHttpRequest object. Last week I had blogged about cross-origin-requests and how it is different from Flash\Silverlight's approach .  With Level 2 specification one can upload the file to the server by passing the file object to the send method. In this post I'll try to explore uploading file using XmlHttpRequest 2 in conjunction with the progress events. I'll also provide a description on the new HTML5 tag -  progress which can be updated while the file is being uploaded to the server. And of course, some ColdFusion code that will show how the file is accepted and stored on the server directory.

Server sent events with HTML5 and ColdFusion

There are several ways to interact with the server apart from the traditional request\response and refresh all protocol. They are polling, long polling, Ajax and Websockets ( pusherapp ). Of all these Ajax and Websockets have been very popular. There is another way to interact with the server such that the server can send notifications to the client using Server Sent Events (SSE) . SSE is a part of HTML5 spec:  http://dev.w3.org/html5/eventsource/

Adding beforeRender and afterRender functions to a Backbone View

I was working on a Backbone application that updated the DOM when a response was received from the server. In a Backbone View, the initialize method would perform some operations and then call the render method to update the view. This worked fine, however there was scenario where in I wanted to perform some tasks before and after rendering the view. This can be considered as firing an event before and after the function had completed its execution. I found a very simple way to do this with Underscore's wrap method.