Getting started

  1. How KO works and what benefits it brings
  2. Downloading and installing

Observables

  1. Creating view models with observables
  2. Working with observable arrays

Computed observables

  1. Using computed observables
  2. Writable computed observables
  3. How dependency tracking works
  4. Pure computed observables
  5. Reference

Bindings

Controlling text and appearance

  1. The visible binding
  2. The text binding
  3. The html binding
  4. The css binding
  5. The style binding
  6. The attr binding

Control flow

  1. The foreach binding
  2. The if binding
  3. The ifnot binding
  4. The with binding
  5. The component binding

Working with form fields

  1. The click binding
  2. The event binding
  3. The submit binding
  4. The enable binding
  5. The disable binding
  6. The value binding
  7. The textInput binding
  8. The hasFocus binding
  9. The checked binding
  10. The options binding
  11. The selectedOptions binding
  12. The uniqueName binding

Rendering templates

  1. The template binding

Binding syntax

  1. The data-bind syntax
  2. The binding context

Creating custom bindings

  1. Creating custom bindings
  2. Controlling descendant bindings
  3. Supporting virtual elements
  4. Custom disposal logic

Components

  1. Overview: What components and custom elements offer
  2. Defining and registering components
  3. The component binding
  4. Using custom elements
  5. Advanced: Custom component loaders

Further techniques

  1. Loading and saving JSON data
  2. Extending observables
  3. Rate-limiting observables
  4. Unobtrusive event handling
  5. Using fn to add custom functions
  6. Extending Knockout's binding syntax

Plugins

  1. The mapping plugin

More information

  1. Browser support
  2. Getting help
  3. Links to tutorials & examples
  4. Usage with AMD using RequireJs (Asynchronous Module Definition)

Using unobtrusive event handlers

In most cases, data-bind attributes provide a clean and succinct way to bind to a view model. However, event handling is one area that can often result in verbose data-bind attributes, as anonymous functions were typically the recommended techinique to pass arguments. For example:

<a href="#" data-bind="click: function() { viewModel.items.remove($data); }">
    remove
</a>

As an alternative, Knockout provides two helper functions that allow you to identify the data associated with a DOM element:

  • ko.dataFor(element) - returns the data that was available for binding against the element
  • ko.contextFor(element) - returns the entire binding context that was available to the DOM element.

These helper functions can be used in event handlers that are attached unobtrusively using something like jQuery’s bind or click. The above function could be attached to each link with a remove class like:

$(".remove").click(function () {
    viewModel.items.remove(ko.dataFor(this));
});

Better yet, this techinique could be used to support event delegation. jQuery’s live/delegate/on functions are an easy way to make this happen:

$(".container").on("click", ".remove", function() {
    viewModel.items.remove(ko.dataFor(this));
});

Now, a single event handler is attached at a higher level and handles clicks against any links with the remove class. This method has the added benefit of automatically handling additional links that are dynamically added to the document (perhaps as the result of an item being added to an observableArray).

Live example: nested children

This example shows “add” and “remove” links on multiple levels of parents and children with a single handler attached unobtrusively for each type of link.

Source code: View

<ul id="people" data-bind='template: { name: "personTmpl", foreach: people }'>
</ul>

<script id="personTmpl" type="text/html">
    <li>
        <a class="remove" href="#"> x </a>
        <span data-bind='text: name'></span>
        <a class="add" href="#"> add child </a>
        <ul data-bind='template: { name: "personTmpl", foreach: children }'></ul>
    </li>
</script>

Source code: View model

var Person = function(name, children) {
    this.name = ko.observable(name);
    this.children = ko.observableArray(children || []);
};

var PeopleModel = function() {
    this.people = ko.observableArray([
        new Person("Bob", [
            new Person("Jan"),
            new Person("Don", [
                new Person("Ted"),
                new Person("Ben", [
                    new Person("Joe", [
                        new Person("Ali"),
                        new Person("Ken")
                    ])
                ]),
                new Person("Doug")
            ])
        ]),
        new Person("Ann", [
            new Person("Eve"),
            new Person("Hal")
        ])
    ]);

    this.addChild = function(name, parentArray) {
        parentArray.push(new Person(name));
    };
};

ko.applyBindings(new PeopleModel());

//attach event handlers
$("#people").on("click", ".remove", function() {
    //retrieve the context
    var context = ko.contextFor(this),
        parentArray = context.$parent.people || context.$parent.children;

    //remove the data (context.$data) from the appropriate array on its parent (context.$parent)
    parentArray.remove(context.$data);

    return false;
});

$("#people").on("click", ".add", function() {
    //retrieve the context
    var context = ko.contextFor(this),
        childName = context.$data.name() + " child",
        parentArray = context.$data.people || context.$data.children;

    //add a child to the appropriate parent, calling a method off of the main view model (context.$root)
    context.$root.addChild(childName, parentArray);

    return false;
});

No matter how nested the links become, the handler is always able to identify and operate on the appropriate data. Using this techinique, we can avoid the overhead of attaching handlers to each individual link and can keep the markup clean and concise.