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:
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.
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
Post a Comment