Knockout

JavaScript library that helps you to create rich, responsive display and editor user interfaces with a clean underlying data model.

  • Pure JavaScript library - works with any server or client-side technology
  • Can be added on top of your existing web application without requiring major architectural changes
  • Compact - around 13kb after gzipping
  • Works on any mainstream browser (IE 6+, Firefox 2+, Chrome, Safari, others)

Model View ViewModel (MVVM)

  1. A model: your application’s stored data.
  2. A view model: a pure-code representation of the data and operations on a UI.
  3. A view: a visible, interactive UI representing the state of the view model.

Model

Just declare any JavaScript object:

var workshop = {
    id: 104,
    title: 'Vehicle Design: Sci-Fi Ship',
    author: 'Paul Christopher',
    price: 39.99
};

View

You can then create a very simple view of this view model using a declarative binding:

<h2>
    <!-- ko text: title --><!-- /ko -->
    <small data-bind="text: '(by ' + author + ')'"></small>
</h2>
<p>Price: $<!-- ko text: price --><!-- /ko --></p>

ViewModel

To activate Knockout, add the following line to a JS code:

ko.applyBindings(workshop);

Result

<h2>
    <!-- ko text: title -->Vehicle Design: Sci-Fi Ship<!-- /ko -->
    <small data-bind="text: '(by ' + author + ')'">(by Paul Christopher)</small>
</h2>
<p>Price: $<!-- ko text: price -->39.99<!-- /ko --></p>

The data-bind

A binding consists of two items, the binding name and value, separated by a colon. Here is an example of a single, simple binding:

Today's message is: <span data-bind="text: message"></span>

An element can include multiple bindings, with each binding separated by a comma.

Cellphone: <input data-bind="value: cellphoneNumber, enable: hasCellphone">

Binding values

<!-- variable (usually a property of the current view model -->
<div data-bind="visible: shouldShowMessage">...</div>

<!-- comparison and conditional -->
The item is <span data-bind="text: price() > 50 ? 'expensive' : 'cheap'"></span>.
<!-- function call and comparison -->
<button data-bind="enable: parseAreaCode(cellphoneNumber()) != '555'">...</button>

<!-- function expression -->
<div data-bind="click: function (data) { myFunction('param1', data) }">...</div>

<!-- object literal (with unquoted and quoted property names) -->
<div data-bind="with: {emotion: 'happy', 'facial-expression': 'smile'}">...</div>

Bindings

  • Controlling text and appearance
  • Control flow
  • Working with form fields
  • Rendering templates

Controlling text and appearance

The "visible" binding

The visible binding causes the associated DOM element to become hidden or visible according to the value you pass to the binding.

<div data-bind="visible: shouldShowMessage">
    You will see this message only when "shouldShowMessage" holds a true value.
</div>

Using functions and expressions to control element visibility.

<div data-bind="visible: items.length > 0">
    You will see this message only when 'items' has at least one member.
</div>

The "text" binding

The text binding causes the associated DOM element to display the text value of your parameter.

Today's message is: <span data-bind="text: message"></span>

The "html" binding

The html binding causes the associated DOM element to display the HTML specified by your parameter.

var viewModel = {
    details: "<em>For further details, view the report <a href='report.html'>here</a>.</em>"
};
    
ko.applyBindings(viewModel);
<div data-bind="html: details"></div>

The "css" binding

The css binding adds or removes one or more named CSS classes to the associated DOM element.

<div data-bind="css: { profitWarning: currentProfit() < 0 }">
    Profit Information
</div>

The "style" binding

The style binding adds or removes one or more style values to the associated DOM element.

<div data-bind="style: { color: currentProfit() < 0 ? 'red' : 'black' }">
    Profit Information
</div>

The "attr" binding

The attr binding provides a generic way to set the value of any attribute for the associated DOM element.

<a data-bind="attr: { href: url, title: details }">
    Report
</a>

Control flow

The "foreach" binding

The foreach binding duplicates a section of markup for each entry in an array, and binds each copy of that markup to the corresponding array item. This is especially useful for rendering lists or tables.

<table>
    <thead>
        <tr>
            <th>First name</th>
            <th>Last name</th>
        </tr>
    </thead>
    <tbody data-bind="foreach: people">
        <tr>
            <td data-bind="text: firstName"></td>
            <td data-bind="text: lastName"></td>
        </tr>
    </tbody>
</table>

The "if" binding

The if binding causes a section of markup to appear in your document (and to have its data-bind attributes applied), only if a specified expression evaluates to true.

<div data-bind="if: displayMessage">Here is a message.</div>

The "ifnot" binding

ifnot is the same as a negated if

<div data-bind="ifnot: someProperty">...</div>

…is equivalent to the following:

<div data-bind="if: !someProperty()">...</div>

The "with" binding

The with binding creates a new binding context, so that descendant elements are bound in the context of a specified object.

<h1 data-bind="text: city"></h1>
<p data-bind="with: coords">
    Latitude: <span data-bind="text: latitude"></span>,
    Longitude: <span data-bind="text: longitude"></span>
</p>

Working with form fields

The "click" binding

The click binding adds an event handler so that your chosen JavaScript function will be invoked when the associated DOM element is clicked.

<button data-bind="click: incrementClickCounter">Click me</button>

The "event" binding

The event binding allows you to add an event handler for a specified event so that your chosen JavaScript function will be invoked when that event is triggered for the associated DOM element.

<div data-bind="event: { mouseover: enableDetails, mouseout: disableDetails }">
    Mouse over me
</div>
<div data-bind="visible: detailsEnabled">
    Details
</div>

The "submit" binding

The submit binding adds an event handler so that your chosen JavaScript function will be invoked when the associated DOM element is submitted.

<form data-bind="submit: doSomething">
    ... form contents go here ...
    <button type="submit">Submit</button>
</form>

The "enable" binding

The enable binding causes the associated DOM element to be enabled only when the parameter value is true.

<p>
    <input type='checkbox' data-bind="checked: hasCellphone" />
    I have a cellphone
</p>
<p>
    Your cellphone number:
    <input type='text' data-bind="value: cellphoneNumber, enable: hasCellphone" />
</p>

The "disable" binding

The disable binding causes the associated DOM element to be disabled only when the parameter value is true. This is useful with form elements like input, select, and textarea.

This is the mirror image of the enable binding.

The "value" binding

The value binding links the associated DOM element’s value with a property on your view model. This is typically useful with form elements such as <input>, <select> and <textarea>.

<p>Login name: <input data-bind="value: userName" /></p>
<p>Password: <input type="password" data-bind="value: userPassword" /></p>

The "checked" binding

The checked binding links a checkable form control — i.e., a checkbox (<input type='checkbox'>) or a radio button (<input type='radio'>) — with a property on your view model.

<p>Send me spam: <input type="checkbox" data-bind="checked: wantsSpam" /></p>

The "options" binding

The options binding controls what options should appear in a drop-down list (i.e., a <select> element) or multi-select list (e.g., <select size='6'>). This binding cannot be used with anything other than <select> elements.

The value you assign should be an array . The <select> element will then display one item for each item in your array.

<p>
    Destination country:
    <select data-bind="options: availableCountries"></select>
</p>

Rendering templates

The "template" binding

The template binding populates the associated DOM element with the results of rendering a template.

<h2>Participants</h2>
Here are the participants:
<div data-bind="template: { name: 'person-template', data: buyer }"></div>
<div data-bind="template: { name: 'person-template', data: seller }"></div>

<script type="text/html" id="person-template">
    <h3 data-bind="text: name"></h3>
    <p>Credits: <span data-bind="text: credits"></span></p>
</script>

Observables

How can KO know when parts of your view model change?

You need to declare your model properties as observables, because these are special JavaScript objects that can notify subscribers about changes, and can automatically detect dependencies.

var workshop = {
    id: 104,
    title: ko.observable('Vehicle Design: Sci-Fi Ship'),
    author: ko.observable('Paul Christopher'),
    price: ko.observable(39.99)
};

View with observables

<p>Title: <input data-bind="value: title"></p>
<p>Author: <input data-bind="value: author, valueUpdate: 'afterkeydown'"></p>
<p>Price: <input data-bind="value: price"></p>

<h2>
    <!-- ko text: title --><!-- /ko -->
    <small data-bind="text: '(by ' + author() + ')'"></small>
</h2>
<p>Price: $<!-- ko text: price --><!-- /ko --></p>
Example link.

Reading and writing observables

ko.observable objects are actually functions.

  • To read the observable’s current value, just call the observable with no parameters. In this example:
    workshop.title();
    // return 'Vehicle Design: Sci-Fi Ship'
  • To write a new value to the observable, call the observable and pass the new value as a parameter:
    workshop.title('Vehicle Design: Sci-Fi Ship 2');
    // change the title to 'Vehicle Design: Sci-Fi Ship 2'

Computed Observables

Workshop = function () {
    var _this = this;
    
    //...
    _this.price = ko.observable(39.99);
    _this.discount = ko.observable(10);
    _this.priceWithDiscount = ko.computed(function () {
        return this.price() - this.price() * (this.discount() / 100)
    }, _this);
};

ko.applyBindings(new Workshop);

Example.

KnockoutJs

By Aleksej Romanovskij

KnockoutJs

  • 1,446