31 October 2010

Model/UI Model/UI Controler javascript patter in short

I've watched today an interesting presentation about functional design patters and I wanted to share it with you my dear and loved reader.

http://www.infoq.com/presentations/Functional-Design-Patterns

Functional programming and functional languages started interesting my from the moment I grokked Javascript. Javascript is an interesting language that blends, in my opinion, good sides from both the object oriented paradigm and the functional paradigm.

Lets face it. Javascript is used mostly for dynamic GUI's on the web and on the mobile platform. User interface events are behavior(action oriented) and the functional style of programming can be very useful in such and environment with some good speed gains. But also the DOM or the UI is an object rich environment , full of entities, relations and etc. Not to mention custom POJSO's (Plain Old Javascript  Object) indicating  varius states our screen can take or coordinating the behavior of other objects in the same screen.

I generally do something like this. First I define my screen entities, those objects that correspond to specific entities which live happily on my screen:


function Person(name, age, sex)
{
  this.Name = name;
  this.Age = age;
  this.Sex = sex;
}



I will then link those two entities to corresponding UIEntities. UIEntities are those POJSO's that group a set of DOMElements and coordinate their UI logic in respect to the current state of the associated Entity object.

For example:

function PersonUIModel(element,person)
{
    this.Element= element;
    this.Name = this.Element.find("input.person-name");
    this.Age = this.Element.find("input.person-age");
    this.Person = person
}

PersonUI.prototype = {
  validateRequired:function(textBox)
  {
      if (textBox.val()=="")
      {
         textBox.css('border','1px solid red');
         return false;
      }
     
      textBox.css('border','');
      return true;    
  },

  Bind:function()
  {
     var object = this;
     //Populate values
     object.Name.val(object.Person.Name);
     object.Age.val(object.Person.Age);
     
     //Bind events
     object.Name.change(function(e){
        if (!object.validateRequired(object.Name)) return;
        object.Person.Name = object.Name.val();
     });
     
     object.Age.change(function(e){
        if (!object.validateRequired(object.Age)) return;
        object.Person.Age= object.Age.val();
     });

     
  }
}


The previous code piece needs some explanations. First for everything UI related I use JQuery, so all examples here that touch the DOM will make use of its innate capabilities.
The constructor of the PersonUIModel object gets two values an JQuery object named Element which corresponds to the actual HTML rendered with the page and attached to the PersonUIModel for controlling. The second argument is the actual Person entity binded to the HTML through the PersonUIModel object.
For ease of use I've extracted the two actual controls I'll need in separate fields of the PersonUIModel object.
The Bind method is used to start the actual two-way databinding between the Person entity and the HTML associated with the PersonUIModel object. Nothing fancy here except for two things.
Just on the start of the Bind method the this indentifier is associated with the object variable. This is done because of the change handlers latter in the code the context of their anonymous methods relies on the PersonUIModel object. If I would make a call like this in the Age.change event handler :

this.Age.change(function(e){
        if (!this.validateRequired(this.Age)) return;
        this.Person.Age= this.Age.val();
     });
I would receive a nice, fat, juicy error indicating the the validateRequired function doesn't exits on the DOMElement. The 'this' inside the event handler actually points to the textbox associated with the Person.Age property (not the JQuery object but the actual DOMElement). But with the power of closures I magically convert my PersonUIModel 'this' to object (or any other variable name) and everything is fine).
The PersonUIModel objects binds to a html code like this:


<div class='person'>
Name: <input class='person-name' type='text' />
Age: <input class='person-age' type='text' /></div>


Now we are left with the question who creates the PersonUIModel instance and binds it to a Person instance and the related HTML code. This is the role of the PeopleUIControler a specific class that coordinates the lifecycle of PersonUIModels.
The PeopleUIControler has the following roles:
  • Finding or creating the appropriate DOMElements on the Screen.
  • Loading and Saving Person models.
  • Binding Person models to DOMElements through PersonUIModel instances.
This means the the Person model is totally decoupled from the UI in which it is located. The PersonUIModel knows only of its little piece of HTML code and its Person model and nothing else. This means that the PersonUIModel doesn't know about other PersonUIModel's on the screen nor does it have any impact on the HTML outside its little realm.

The PeopleUIControler will look like this:

function PeopleUIControler()
{
      this.People = [];
      this.PeopleUI = [];
}

PeopleUIControler.prototype = {
  btnSave: null,
  btnLoad: null,
  pnlPeople: null,
  Load:function(){
      //Ajax loading ... loads a bunch of Person entities
      this.People = bunchOfPersonEntitesLoadedThroughAjax;
  },

  Save: function(){
     for(var i=0;i < this.People.length;i++)
     {
         var person = this.People[i];
         //Save the person through AJAX
         //....
     }
  },
  Bind:function(){
     var object = this;

     this.pnlPeople=$('.panel-people');

     this.btnSave=$('.person-save');
     this.btnSave.click(function(e){
        object.Save();
     });

     this.btnLoad=$('.person-load');
     this.btnLoad.click(function(e){
        object.Load();
     });

    //Load entities synchronously (just for the sake of the argument)
    this.Load();

    var html="<div class='person'>
Name: <input class='person-name' type='text' />
Age: <input class='person-age' type='text' /></div>";

    for(var i=0;i < this.People.length;++){
      var personUI = new PersonUI($(html), this.People[i]);
      personUI.Bind();
      this.PeopleUI.push(personUI);

      this.pnlPeople.append(personUI.Element);
    }
  }
}

The code for the PeopleUIControler is similar to that of the PersonUIModel. The controled has two methods Load and Save for loading and saving Person entities to a permanent source and a Bind method which binds all elements. The PeopleUIControler ties it self to an html structure that sports a panel for PersonUIModels and two buttons one for loading and saving. When first loading PersonUIModels each model is binded to a generated piece of html and then the same html is appended to the PeopleUIControler panel. The final html code used by the PeopleUIControler would look like this:
<div class='panel-people'></div>
<div>
<button class='person-save'>Save</button>
<button>load<="" <="" button>="" class="person-load" div>=""></button>

No comments:

Post a Comment