ict.ken.be

Delivering solid user friendly software solutions since the dawn of time.

Knockout.js

Categories: Javascript

http://knockoutjs.com/ documentation by Steve Sanderson

  • Automatic UI Refresh MVVM pattern for javascript
  • Automatic dependency tracking
  • Uses jQuery templates
  • Declarative bindings Integrated templating
  • Upgrade JSON to observable by using Knockout.mapping
  • DependentObservable => Computed

Overview

var myViewModel = { personName: ko.observable('Ken'), personAge:103 };
The name is <span data-bind="text: personName"></span>

ko.applyBindings(myViewModel);
ko.applyBindings(myViewModel, document.getElementById('someElementId'));

myViewModel.personName(); to get
myViewModel.personName('Patrycja'); to set
myViewModel.personName('Patrycja').personAge(50);

var subscription = myViewModel.personName.subscribe(function(newValue) { alert(newValue); });
subscription.dispose();

this.fullName = ko.computed(function() { return this.firstName() + " " + this.lastName(); }, this);
function AppViewModel() {
    var self = this;
    self.firstName = ko.observable('Bob');
    self.lastName = ko.observable('Smith');     
    self.fullName = ko.computed(function() {
        return self.firstName() + " " + self.lastName();
    });
}
this.fullName = ko.computed({
        read: function () {
            return this.firstName() + " " + this.lastName(); 
        },
        write: function (value) {
            var lastSpacePos = value.lastIndexOf(" ");
            if (lastSpacePos > 0) { // Ignore values with no space character
                this.firstName(value.substring(0, lastSpacePos)); // Update "firstName"
                this.lastName(value.substring(lastSpacePos + 1)); // Update "lastName"
            }
        },
        owner: this
});
ko.applyBindings(new MyViewModel());
read, write, owner, deferEvaluation, disposeWhen, disposeWhenNodeIsRemoved

ko.isComputed, ko.isObservable, ko.isWriteableObservable

var myObservableArray = ko.observableArray([{ name: "Bungle", type: "Bear" }]);
myObservableArray.push('Some value'); // Adds the value and notifies observers
alert('The length of the array is ' + myObservableArray().length);
alert('The first element is ' + myObservableArray()[0]);
myObservableArray.indexOf('Blah'); //zero-based and -1 if not found
myObservableArray.slice(...);
myObservableArray.push('Some new value'); //adds a new item to the end of array
myObservableArray.pop(); //removes the last value from the array and returns it
myObservableArray.unshift('Some new value'); //inserts a new item at the beginning of the array
myObservableArray.shift(); //removes the first value from the array and returns it
myObservableArray.reverse(); //reverses the order of the array
myObservableArray.sort(); //sorts the array contents.
myObservableArray.sort(function(left, right) 
	{ return left.lastName == right.lastName ? 0 : 
	(left.lastName < right.lastName ? -1 : 1) });
myObservableArray.splice(1, 3);
myObservableArray.remove(someItem); 
myObservableArray.remove(function(item) { return item.age < 18 });
myObservableArray.removeAll(['Chad', 132, undefined]);
myObservableArray.removeAll();

visible, text, html, css, style, attr
<div data-bind="css: { profitWarning: currentProfit() < 0 }">...</div>
<div data-bind="style: { color: currentProfit() < 0 ? 'red' : 'black' }">...</div>
<a data-bind="attr: { href: url, title: details }">...</a>

foreach, if, ifnot, with
<div data-bind="if: displayMessage">...</div>

click, event, submit, enable, disable, value, hasfocus, checked, options, selectedOptions, uniqueName
<button data-bind="click: incrementClickCounter">Click me</button>
<div data-bind="event: { mouseover: enableDetails, mouseout: disableDetails }">...</div>
<input data-bind="hasfocus: isSelected" />

ko.bindingHandlers.yourBindingName = { ... };

<div data-bind="template: { name: 'person-template', data: buyer }"> </div>
<script type="text/html" id="person-template">
    <h3 data-bind="text: name"></h3>
    <p>Credits: <span data-bind="text: credits"></span></p>
</script>

.extend({ throttle: 500 }); //delayed binding

var viewModel = ko.mapping.fromJS(data);
ko.mapping.fromJS(data, viewModel);
var unmapped = ko.mapping.toJS(viewModel);

Example

var viewModel = {
	firstName: ko.observable("Ken"),
	lastName: ko.observable("Van Gilbergen"),
	friends: ko.observableArray([new friend("Steve"), new friend("Annie")]),
	addFriend: function() {
		this.friends.push(new friend("Tim"));
	},
	save: function() {
		$.ajax({
			url: "@Url.Action("Save")",
			type: "post",
			data: ko.toJSON(this),
			contentType: "application/json",
			success: function (result) { alert(result.message) }
		});
	}
};

viewModel.fullName = ko.computed(
function() {
	return this.firstName() + " " + this.lastName();
}, viewModel);

ko.applyBindings(viewModel);

<input data-bind="value: firstName" />

function friend(name)
{
	return {
		name: ko.observable(name),
		isOnTwitter: ko.observable(false),
		twitterName: ko.observable,
		remove: function ()
		{
			viewModel.friends.remove(this);
		}
	};
}

<div data-bind="template: 'friendsTemplate'"> </div>
<script id="friendsTemplate" type="text/html">
	<ul>
	{{each(index, friend) friends}}
		<ul><li>${ friend.name } - ${ new Date }</li></ul>
		<label><input type="checkbox" data-bind="checked: isOnTwitter" /> Is on twitter</label>
		<input data-bind="value: twitterName, visible: isOnTwitter" />
	{{/each}}	
	</ul>
</script>
<button data-bind="click: addFriend, enabled: friends().length < 5">Add Friend</button>

<script id="friendsTemplate2" type="text/html">
	<ul><ul><li>${ data.name } - ${ new Date }</li></ul>
	<button data-bind="click: remove">Remove
</script>

public JsonResult Save(Person person)
{
	...
	return Json(new { message });
}

public class Person
{
	public string FirstName { get; set;}
	public ICollection Friends { get; set; }     }