Sunday, October 27, 2013

knockout js (and others)

For a side project I'm involved with, one of the requirements is to have a web site that is quite slick, eschewing post backs wherever possible. I'm treating each distinct page of the web site like an SPA - being self contained and driven by client side script.

So I'm using a combination of JQuery, JQuery UI and knockout. I appreciate knockout, but it does take some time and patience to craft a working site with it. I'm primarily using it for the templating implementation - I looked at others in this space, such as Handlebars.js and Mustache.js, but settled on knockout.

Here is an extant example (using, horror of horrors, a table - after creating a css implementation that just felt wrong!), binding an 'exercise' object to a table row. I'm using a private HTML 5 style attribute (data-classify) for my own purposes.

 <tbody id='exerciseBody' data-bind='foreach: exercise'>  
  <tr data-bind="attr: { 'data-classify': ex.Definition }">  
  <td><input type="checkbox"   
       data-bind="attr: {   
                name: checkBoxName, value: ex.Id },   
                checked: isChecked"/>Select  
  </td>  
  <td><img width="128" height="72"   
       data-bind="attr: { src: '/AJAX/Image/' + ex.ImageIds[0] }" />  
  </td>  
  <td><img width="128" height="72"  
       data-bind="attr: { src: '/AJAX/Image/' + ex.ImageIds[1] }" />  
  </td>  
  <td data-bind="text: ex.Description">  
  </td>  
  </tr>  
 </tbody>  

This is the view model that is supplied as a member of the bound collection:

  var SelectionViewModel =   
   function (ex, isSelected, updateFn) {  
    this.ex = ex;  
    this.checkBoxName = 'c' + ex.Id;  
    this.isChecked = ko.observable(isSelected);  
    this.isChecked.subscribe(function (s) {  
     (updateFn || $.noop)(s, this.ex);  
    }, this);  
 };  

As can be seen, this object subscribes to change events on the isChecked member, which is bound to the 'checked' property of the check box.

An interesting piece of JQuery behaviour is used as well; wild card matching on an element attribute. The JS code below shows this in action - in the else, we locate all elements that have and do not have a specific term held in their (private) data-classify attribute.

 self.selectedTerm.subscribe(function (s) {  
  var searchBase = $("#exerciseBody");  
  if (self.selectedTerm() === anyTerm)  
   searchBase.find('tr').show();  
  else {  
   searchBase.find("tr:not([data-classify*='" + self.selectedTerm() + "'])").hide();  
   searchBase.find("tr[data-classify*='" + self.selectedTerm() + "']").show();  
  }  
 });  

No comments: