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
  5. Preprocessing: Extending the binding syntax

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. Deferred updates
  4. Rate-limiting observables
  5. Unobtrusive event handling
  6. Using fn to add custom functions
  7. Microtasks
  8. Asynchronous error handling

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)

Writable computed observables

Beginners may wish to skip this section - writable computed observables are fairly advanced and are not necessary in most situations

Normally, computed observables have a value that is computed from other observables and are therefore read-only. What may seem surprising, then, is that it is possible to make computed observables writable. You just need to supply your own callback function that does something sensible with written values.

You can use a writable computed observable exactly like a regular observable, with your own custom logic intercepting all reads and writes. Just like observables, you can write values to multiple observable or computed observable properties on a model object using chaining syntax. For example, myViewModel.fullName('Joe Smith').age(50).

Writable computed observables are a powerful feature with a wide range of possible uses.

Example 1: Decomposing user input

Going back to the classic 鈥渇irst name + last name = full name鈥 example, you can turn things back-to-front: make the fullName computed observable writable, so that the user can directly edit the full name, and their supplied value will be parsed and mapped back to the underlying firstName and lastName observables. In this example, the write callback handles incoming values by splitting the incoming text into 鈥渇irstName鈥 and 鈥渓astName鈥 components, and writing those values back to the underlying observables.

First name:
Last name:
Hello,

Source code: View

    <div>First name: <span data-bind="text: firstName"></span></div>
    <div>Last name: <span data-bind="text: lastName"></span></div>
    <div class="heading">Hello, <input data-bind="textInput: fullName"/></div>

Source code: View model

    function MyViewModel() {
        this.firstName = ko.observable('Planet');
        this.lastName = ko.observable('Earth');

        this.fullName = ko.pureComputed({
            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());

This is the exact opposite of the Hello World example, in that here the first and last names are not editable, but the combined full name is editable.

The preceding view model code demonstrates the single parameter syntax for initializing computed observables. See the computed observable reference for the full list of available options.

Example 2: Selecting/deselecting all items

When presenting the user with a list of selectable items, it is often useful to include a method to select or deselect all of the items. This can be represented quite intuitively with a boolean value that represents whether all items are selected. When set to true it will select all items, and when set to false it will deselect them.

Produce

Source code: View

    <div class="heading">
        <input type="checkbox" data-bind="checked: selectedAllProduce" title="Select all/none"/> Produce
    </div>
    <div data-bind="foreach: produce">
        <label>
            <input type="checkbox" data-bind="checkedValue: $data, checked: $parent.selectedProduce"/>
            <span data-bind="text: $data"></span>
        </label>
    </div>

Source code: View model

    function MyViewModel() {
        this.produce = [ 'Apple', 'Banana', 'Celery', 'Corn', 'Orange', 'Spinach' ];
        this.selectedProduce = ko.observableArray([ 'Corn', 'Orange' ]);
        this.selectedAllProduce = ko.pureComputed({
            read: function () {
                // Comparing length is quick and is accurate if only items from the
                // main array are added to the selected array.
                return this.selectedProduce().length === this.produce.length;
            },
            write: function (value) {
                this.selectedProduce(value ? this.produce.slice(0) : []);
            },
            owner: this
        });
    }
    ko.applyBindings(new MyViewModel());

Example 3: A value converter

Sometimes you might want to represent a data point on the screen in a different format than its underlying storage. For example, you might want to store a price as a raw float value, but let the user edit it with a currency symbol and fixed number of decimal places. You can use a writable computed observable to represent the formatted price, mapping incoming values back to the underlying float value:

Enter bid price:
(Raw value: )

Source code: View

    <div>Enter bid price: <input data-bind="textInput: formattedPrice"/></div>
    <div>(Raw value: <span data-bind="text: price"></span>)</div>

Source code: View model

    function MyViewModel() {
        this.price = ko.observable(25.99);

        this.formattedPrice = ko.pureComputed({
            read: function () {
                return '$' + this.price().toFixed(2);
            },
            write: function (value) {
                // Strip out unwanted characters, parse as float, then write the 
                // raw data back to the underlying "price" observable
                value = parseFloat(value.replace(/[^\.\d]/g, ""));
                this.price(isNaN(value) ? 0 : value); // Write to underlying storage
            },
            owner: this
        });
    }

    ko.applyBindings(new MyViewModel());

Now, whenever the user enters a new price, the text box immediately updates to show it formatted with the currency symbol and two decimal places, no matter what format they entered the value in. This gives a great user experience, because the user sees how the software has understood their data entry as a price. They know they can鈥檛 enter more than two decimal places, because if they try to, the additional decimal places are immediately removed. Similarly, they can鈥檛 enter negative values, because the write callback strips off any minus sign.

Example 4: Filtering and validating user input

Example 1 showed how a writable computed observable can effectively filter its incoming data by choosing not to write certain values back to the underlying observables if they don鈥檛 meet some criteria. It ignored full name values that didn鈥檛 include a space.

Taking this a step further, you could also toggle an isValid flag depending on whether the latest input was satisfactory, and display a message in the UI accordingly. There鈥檚 an easier way of doing validation (explained below), but first consider the following example, which demonstrates the mechanism:

Enter a numeric value:
That's not a number!
(Accepted value: )

Source code: View

    <div>Enter a numeric value: <input data-bind="textInput: attemptedValue"/></div>
    <div class="error" data-bind="visible: !lastInputWasValid()">That's not a number!</div>
    <div>(Accepted value: <span data-bind="text: acceptedNumericValue"></span>)</div>

Source code: View model

    function MyViewModel() {
        this.acceptedNumericValue = ko.observable(123);
        this.lastInputWasValid = ko.observable(true);

        this.attemptedValue = ko.pureComputed({
            read: this.acceptedNumericValue,
            write: function (value) {
                if (isNaN(value))
                    this.lastInputWasValid(false);
                else {
                    this.lastInputWasValid(true);
                    this.acceptedNumericValue(value); // Write to underlying storage
                }
            },
            owner: this
        });
    }

    ko.applyBindings(new MyViewModel());

Now, acceptedNumericValue will only ever contain numeric values, and any other values entered will trigger the appearance of a validation message instead of updating acceptedNumericValue.

Note: For such trivial requirements as validating that an input is numeric, this technique is overkill. It would be far easier just to use jQuery Validation and its number class on the <input> element. Knockout and jQuery Validation work together nicely, as demonstrated on the grid editor example. However, the preceding example demonstrates a more general mechanism for filtering and validating with custom logic to control what kind of user feedback appears, which may be of use if your scenario is more complex than jQuery Validation handles natively.