Friday, October 26, 2012

How to write JavaScript in MVC style

Intent
==============================================================================
This tutorial explains a way to implement MVC (Model View Controller) pattern in client script using pure JavaScript. Idea is to highlight basic fundamentals of implementing MVC in client code. If you grab these basics, you can easily create advance implementations with lesser code using various libraries like JQuery and Knockout etc.

Quick brush up on MVC

Instead of getting theoretical, I'll put it this way:

View: These are the HTML elements in your UI - TextBoxes, HiddenBoxes, Images, Tables etc.
For example, in Google home page, view elements includes Search TextBox, Search Button, Search result div container, Suggestions display div container etc.

Model: Model represents data which drives functionality of screen. It may or may not have 1:1 mapping with your view elements. For example, in Google search page, model may contain an array of autoSuggest words or an array of search results in JSON or the searched keyword.

Controllers: Controller is a functional unit containing a bunch of Action Methods where each Action Method handles a functional requirement. DO NOT correlate action methods with control events like OnClick, OnChange, OnKeyPress. Rather consider them as what features are being provided by page to end user. Continuing with Google example, expected actions include search, get suggestions etc. How the result of this actions will impact UI is something for view to decide. For example a controller may call server and get results but it won't directly update UI elements. Yes, it can call a view function to do so.

To give it a more practical touch, Just for Analogy consider Google search page and think if it was implemented using MVC. With aforesaid definitions, very likely Model, View and Controller would've been like following:
javascript mvc


Rules of the Game

It is very likely that as your code grows, you may mix-n-match elements of MVC. To avoid deviation from spirit of MVC, we'll follow a few rules:

1. Any function that alters View should be in View class
2. We'll try to use models in controller rather than UI controls directly. If we want any change in UI, we'll call a function in View (as mentioned in pt.1). This  point also means that we would need to associate our model properties with UI controls. For example, if we have a property SearchKeyword, then it should always give latest value of search textbox.
3. Keep controller function names more function than UI controls specific. For example SubmitForm or doSearch is better than btnSubmitClick.
4. Only controller has idea about how model, view and user gestures integrate.
CODE !! CODE !! CODE !!

Bare basic skeleton
 var mvcPage = (function () {
                  var controller = {}; //define all contoller actions
                  var view = {}; //define all view properties and actions
                  var model = {}; //define models
               return {
                   initialize: function () {} //only method accessible outside
                }
        });
and at bottom of page (so that all controls have rendered)
mvcPage().initialize();//

If you find this skeleton difficult to understand (Not the MVC terminology but the Javascript style itself), I encourage to first read How to write JavaScript Functions in Different Styles first.

Extending above skeleton in context of Google example to make things more clear
var mvcPage = (function () {
   var controller = {

            //For example, do search (irrespective on what triggers it)
            DoSearch: function () {

            //make ajax call.. with model.searchKeyword

            //get data.. bla bla bla! and show results
            view.showResults()
          }
    };
   
   var view = {

            //associate view properties to UI elements, for example:
            searchBox: document.getElementById('txtKeywordSearch'),
    
           //Define UI manipulating actions. For example,
           // Amethod to display search results

            showResults: function () {
                 //create div.. append result.. bla bla bla!
            },
        };

   var model = {

            //Define pieces of details we need for our page
            searchKeyword: '',
            searchResults: []

        };

   //bind events to objects in view
   function bindEvents() {

         //when submit button is clicked, do search
         view.searchBox.onclick = controller.DoSearch;
    }

   
   return {
   
        //initialize is the only method accessible outside  
        initialize: function () {
                bindEvents();
           }

     };
});

Exercise:

Now, If you're clear with concepts and sample code above, then try a Exercise.

Create a page with a textbox and a button. User will input name in textbox and click button. Every time user clicks button, name in the textbox should get appended in a comma separated names list and displayed. Try the following working Demo of what I mean:


Enter a name:
Names: