-
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)
Development order: 1. View, 2. ViewModel, 3. Ask server dev to implement Model. UI dev is master, server dev is slave.public class RegistrationDto {
public string Login {get; set;}
public string Password {get; set;}
}
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.
}
};
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