• Architecture
  • Observable
  • Binding
  • Debug
  • Homework

Why ?

  • Work separation (layout, programming)
  • Easy to roll down to spaghetti code
  • Lack of binding html <=> js (input value <=> js value)
  • Computed field (totalSum, fullName)

 Architecture

Model View ViewModel

  • View (dev it 1st)                                                                                                          
    Login: <input type="email" data-bind="value: email" /><br />
    Password: <input type="password" data-bind="value: password" /><br />
    <button data-bind="click: register">Register</button>
  • ViewModel (is view reflection, dev it 2nd)
    function RegistrationViewModel() {
    var self = this;
    this.email = ko.observable("");
    this.password = ko.observable("");
    this.register = function() {
    var model = {Login: self.email(), Password: self.password()};
    $.ajax({url: "/Api/RegisterUser", type: "POST", data: model});
    };
    }
  • Model (depend on ViewModel if possible, 3rd)
    public class RegistrationDto {
    public string Login {get; set;}
    public string Password {get; set;}
    }
    Development order: 1. View, 2. ViewModel, 3. Ask server dev to implement Model. UI dev is master, server dev is slave.

ServiceAgent

this.register = function() {
var model = {Login: self.email(), Password: self.password()};
$.ajax({url: "/Api/RegisterUser", type: "POST", data: model});
};
Oops, I need to test it
function RegistrationViewModel() {
var self = this; this.serviceAgent = new ServiceAgent();
this.email = ko.observable("");
this.password = ko.observable("");
this.register = function() {
var model = {Login: self.email(), Password: self.password()};
self.serviceAgent.registerUser(model);
};
}
function ServiceAgent() {
this.registerUser = function(model) {
return $.ajax({url: "/Api/RegisterUser", type: "POST", data: model});
};
}

ViewModel

  • class - component                            
    • field - data, state
    • property - calculation
    • method - action, behavior

Observable

  • Create field                                                                                                
    var login = ko.observable("");
    
  • Read value
    var val = login();
    
  • Write value
    login("Admin");
    
  • Listen for value change
    var subscription = login.subscribe(function(newValue) { 
    /* ask server to check uniqueness */
    });
  • Clean up
    subscription.dispose();
    

array

  • Create array field                                                                                                 
    var products = ko.observableArray([]);
    
  • Read
    var arrayValue = products();
    var value = products()[0];
    
  • Add value
    products.push(product);
    
  • Remove value
    products.remove(product);
    

Computed

  • Create read-only property                                                                           
    var fullName = ko.computed(function() {
    return firstName() + " " + lastName();
    });
  • Create writeable property
    var fullName = ko.computed({
    read: function () {
    return firstName() + " " + lastName();
    },
    write: function (value) {
    var lastSpacePos = value.lastIndexOf(" ");
    firstName(value.substring(0, lastSpacePos));
    lastName(value.substring(lastSpacePos + 1));
    } });
  • Read, write,  listen, clean up is the same
  • Demo

Binding

  • Glue together HTML element and model
  • Accessing DOM only inside binding
    ko.applyBindings(new RegistrationViewModel());
  • Demo


Custom binding

  • use UI external component (bootstrap, keyfilter, select2)
  • access DOM from ViewModel (width, scrollTop)
  • reuse UI logic (slideFromRight)
  • add behavior (glueToBottom, executeOnEnter)
ko.bindingHandlers["slideFromRight"] = {
init: function(element, valueAccessor) {
// will be called when the binding is first applied to an element
},
update: function(element, valueAccessor) {
// will be called when the binding is first applied to an element,
// and again whenever the associated observable changes value.
}
};

slideVisible example

Debug

  • don't  use browser plugins
  • ko.dataFor($0)
  • remove binding by adding __
  • field.getSubscriptionsCount()
  • ko.getUnusedFields($0)
  • unit tests

Homework

  • Product list
    • build View, ViewModel, ServiceAgent
    • service agent returns hardcoded model
    • user can change product name
    • user can remove an item
    • user can save changed data


KnockoutJS

By Vladimir Gaevoy

KnockoutJS

  • 1,707