Knock Me Out

Thoughts, ideas, and discussion about Knockout.js

10 Things to Know About KnockoutJS on Day One

| Comments

I have been working with Knockout.js on a daily basis now for about six months. Much of that time has been spent trying to help folks on the KO forums and StackOverflow troubleshoot problems and brainstorm ideas. Here is a list of some areas that I feel are commonly misunderstood or overlooked by people that are starting out with Knockout.

1 - How to set and read observables

Observables are functions. The actual value and subscribers to the observable are cached internally by the function. You set an observable’s value by passing the new value as the only argument to the function and you read the value by passing no arguments.

1
2
3
var name = ko.observable("Bob"); //initialize with a value
name("Ted"); //set it to a new value
alert(name()); //read the value

Question: If you need to reference it as a function to read the value, then how come in a data-bind attribute you typically specify just the property name?

Answer: Most bindings will call ko.utils.unwrapObservable on the value passed to it, which will safely return the value for both observables and non-observables. However, in a binding if you use an observable in an expression, then you need to reference it as a function. Likewise, in code you typically need to reference your observables as functions, unless you actually want to pass the observable itself (not the value).

1
2
3
<div data-bind="visible: someFlag">...</div>

<div data-bind="visible: !someFlag()">...</div>

2 – Templates are flexible

The template binding is quite flexible. Here are a few things that you will likely want to do with it before long:

The template binding accepts a data argument that allows you to control the context of the binding. This is handy for simplifying references to nested content. It also accepts an if parameter that helps to handle when the observable value may be null.

1
<div data-bind="template: { name: 'nestedTmpl', 'if': myNestedObject, data: myNestedObject }"></div>

The template binding also accepts a foreach parameter to loop through items in the array passed to it. If the array is observable and changes, then Knockout efficiently adds or removes DOM nodes appropriately rather than re-rendering the entire template (as it does when using jQuery Templates syntax).

1
<ul data-bind="template: { name: 'itemTmpl', foreach: items }"></ul>

The template binding accepts a templateOptions parameter that lets you pass additional data in to your template. This could include methods or observables from your view model. This is crucial for cases where scope would prevent you from accessing the proper variables from inside your template. More info in this post.

1
<ul data-bind="template: { name: 'itemTmpl', foreach: items, templateOptions: { selected: selectedItem } }"></ul>

update: 5/1/2012 - templateOptions is only supported when using jQuery Templates and is not used in KO native templates that were introduced in KO 2.0

Another useful feature to consider is that the name of the template that is applied can be dynamic. More details in this post.

3 – The basic rules of computed observables

By default, the value of a computed observable is determined at the time of creation. However, this behavior can be controlled by creating the computed observable using object syntax and passing in deferEvaluation: true.

1
2
3
4
5
6
7
8
9
viewModel.total = ko.computed({
    read: function() {
       var result = 0;
        ko.utils.arrayForEach(viewModel.items(), function(item) {
            result += item.amount();
        });
    },
    deferEvaluation: true  //don't evaluate until someone requests the value
}, viewModel);

A computed observable will be re-evaluated whenever one of the observables that it accessed in its last evaluation changes. Dependency detection is done each time that the computed observable is evaluated. In the snippet below, if enabled is true, then it will not depend on disabledHelp. However, if enabled becomes false, then it will no longer depend on enabledHelp and will start depending on disabledHelp.

1
2
3
4
//this computed observable will always depend on this.enabled and will additionally depend on either this.enabledHelp or this.disabledHelp.
viewModel.helpText = ko.computed({
    return this.enabled() ? this.enabledHelp() : this.disabledHelp();
}, viewModel);

4 - Manual subscriptions are quite useful

Manual subscriptions give you a chance to programmatically react to an observable changing. This is great for setting defaults and triggering AJAX requests. You are able to manually subscribe to observables, observableArrays, and computed observables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//trigger an AJAX request to get details when the selection changes
viewModel.selectedItem.subscribe(function(newValue) {
    $.ajax({
        url: '/getDetails',
        data: ko.toJSON({
            id: newValue.id
        }),
        datatype: "json",
        contentType: "application/json charset=utf-8",
        success: function(data) {
            viewModel.details(data.details);
        }
    });
});

5 - “this” is really important.

Understanding the value of this when your functions are called is even more important in Knockout than normal, because you are referencing functions in bindings that are being executed indirectly.

Suppose, our view model has a method that alerts on a name property:

1
2
3
viewModel.whoAmI = function() {
   alert(this.name);
};

If I use this method on a click binding within a template that is looping through an array of items, then this will actually be my item and not the viewModel. This is because viewModel.whoAmI just points to the function and Knockout executes it under the context of the data being bound.

1
2
3
4
5
6
<ul data-bind="template: { name: 'itemsTmpl', foreach: items }"></ul>
<script id="itemsTmpl" type="text/html">
     <li>
         <a href="#" data-bind="click: viewModel.whoAmI">Who am I?</a>
     </li>
</script>

Knockout does provide an implementation of bind that you can use on any function, when you want to guarantee the context that it will run under. In this case, it would look like:

1
2
3
viewModel.whoAmI = function() {
   alert(this.name);
}.bind(viewModel);

Manual subscriptions and computed observables do take in a second argument to control the value of this when they are executed.

1
2
3
4
5
6
7
8
9
viewModel.fullName = ko.dependentObservable(function() {
    return this.firstName() + " " + this.lastName();
}, viewModel);

viewModel.gratuityAdded.subscribe(function(newValue) {
    if (newValue) {
       this.total(this.total() * 1.15);
    }
}, viewModel);

6 - KO utility functions (ko.utils)

Get to know the Knockout utility functions, as you will probably start to use them throughout your Knockout code. They are quite useful for navigating and manipulating your view model, although you might already be using similar functions from other libraries. This post describes some of the more useful ones.

Also, take a look at their source code, as their implementations are fairly straightforward.

7 - An observableArray is just an extended observable

An observableArray is actually just an observable. They follow the same rules and have the same features as observables.

An observableArray also has some extra methods added to it to perform basic array operations. These functions perform their action on the underlying array and then notify subscribers that there was a change. These methods include pop, push, reverse, shift, sort, splice, and unshift.

In addition to the those operations, there are several other methods added for common operations. These include remove, removeAll, destroy, destroyAll, replace, and indexOf (which I always forget).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//remove an item
items.remove(someItem);

//remove all items with the name "Bob"
items.remove(function(item) {
    return item.name === "Bob"
});
//remove all items
items.removeAll();

//pass in an array of items to remove
items.removeAll(itemsToRemove)

//retrieve the index of an item
items.indexOf(someItem);

//replace an item
item.replace(someItem, replaceItem);

Note: destroy/destroyAll work like remove/removeAll, except they only mark the items as destroyed and don’t actually remove them.

8 - Custom bindings need not be a last resort

There seems to be a misconception that custom bindings should only be considered if there is no other way to accomplish the desired functionality with the default bindings. I actually think that custom bindings can be used in a variety of situations and should be considered one of the normal tools that you use along with dependentObservables and manual subscriptions. Besides helping control custom behavior and/or interacting with 3rd party components, they can also be used to simplify your bindings by encapsulating multiple behaviors.

The most basic custom binding is usually a wrapper to an existing binding.

1
2
3
4
5
6
7
ko.bindingHandlers.fadeInText = {
    update: function(element, valueAccessor) {
        $(element).hide();
        ko.bindingHandlers.text.update(element, valueAccessor);
        $(element).fadeIn('slow');
    }
};

Anytime that you find your JavaScript code starting to deal with DOM elements, you will likely want to consider placing it into a binding. Given the element, your data, and the values passed to the binding, you can really take control of anything with Knockout. It is also useful to take a look at the existing bindings in the source code, as they are not too hard to digest and provide patterns that can be used in custom bindings.

9 - ko.toJSON has multiple uses

ko.toJSON can be used to convert objects that include observables to a JSON string that is ready to send to the server.

Also, it can be really useful for debugging. Put a div at the bottom of your page and display some or all of your viewModel using ko.toJSON to get a live preview of how changes in your UI are affecting the underlying data. No need for console.logs or alerts.

1
2
3
<hr />
<h2>Debug</h2>
<div data-bind="text: ko.toJSON(viewModel)"></div>

10 – The KO forums are a great place to ask for help, look for solutions, and to share ideas

Stop by the Knockout forums and share your problems, ideas, and thoughts. It is always interesting to hear new perspectives and new ways that people want to use Knockout. You will generally get civil, helpful, and timely responses to your posts.

Lazy Loading an Observable in KnockoutJS

| Comments

When using Knockout, often it is not efficient to populate the entire view model when a page loads. Typically, this means triggering AJAX requests to retrieve additional data based on changes to the state of the view model. The pattern that I usually use for this process is to create a manual subscription to an observable that represents the currently selected item/category/tab. When the observable changes, I make a request to fill out the rest of its data in the subscription.

I thought that it would be nice to encapsulate some of this logic into a reusable object that does not actually retrieve its data until it is accessed (typically this would mean when it is actually bound). This can be accomplished pretty easily with an augmented computed observable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//an observable that retrieves its value when first bound
ko.onDemandObservable = function(callback, target) {
    var _value = ko.observable();  //private observable

    var result = ko.computed({
        read: function() {
            //if it has not been loaded, execute the supplied function
            if (!result.loaded()) {
                callback.call(target);
            }
            //always return the current value
            return _value();
        },
        write: function(newValue) {
            //indicate that the value is now loaded and set it
            result.loaded(true);
            _value(newValue);
        },
        deferEvaluation: true  //do not evaluate immediately when created
    });

    //expose the current state, which can be bound against
    result.loaded = ko.observable();
    //load it again
    result.refresh = function() {
        result.loaded(false);
    };

    return result;
};

Nothing fancy or complicated at all about this code. A few notes on it:

  • The key is passing the little-known flag, deferEvaluation: true, to the computed observable. This signals it to not do an evaluation until someone requests its value. So, our AJAX request will not go out until it is bound.
  • When you define this object, you pass in a function that will retrieve the data and a target to define the execution context. In this implementation, it is expected that the callback sets the observable asynchronously. Otherwise, our computed observable would depend on itself and potentially try to evaluate itself recursively. The callback could certainly be executed in a setTimeout, but it didn’t seem necessary for how I use it.
  • You can set, read, and bind to this object, just like any observable. It uses a writeable computed observable in front of the actual observable to allow greater control over the reads and writes.
  • Additionally, it exposes a sub-observable named loaded that you can bind against in your UI. You could use this to indicate that something is currently loading or to display a refresh button after it has been loaded.
  • There is also a refresh method that simply sets the loaded flag to false, which causes the dependentObservable to be re-evaluated.
  • You can also manually set the value of this observable, which will automatically set loaded to true. In my sample, the content of the first tab is provided when the page is loaded, so I do not want to retrieve it via AJAX.

Here is a sample scenario that uses this technique:

Link to sample on jsFiddle.net

Here is an additional sample that uses bootstrap tabs:

Link to sample on jsFiddle.net

Fun With Highlighting in KnockoutJS

| Comments

A question came up on the KO forums the other day about how to highlight part of some text that was being bound. This sounded to me like an interesting place to apply a custom binding. My idea to solve this issue was to use a highlightedText binding that looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
ko.bindingHandlers.highlightedText = {
    update: function(element, valueAccessor) {
        var options = valueAccessor();
        var value = ko.utils.unwrapObservable(options.text);
        var search = ko.utils.unwrapObservable(options.highlight);
        var css = ko.utils.unwrapObservable(options.css);
        if (options.sanitize) {
            value = $('<div/>').text(value).html(); //could do this or something similar to escape HTML before replacement, if there is a risk of HTML injection in this value
        }
        var replacement = '<span class="' + css + '">' + search + '</span>';
        element.innerHTML = value.replace(new RegExp(search, 'g'), replacement);
    }
};

You would use the binding like:

1
<div data-bind="highlightedText: { text: details, highlight: match, css: 'highlight' }"></div>

The code is not particularly clever and could likely be improved, but to me it at least illustrates how easy it is to write custom bindings that are useful and powerful. In this case, the actual text, the search string, and the css class to apply could all be observables. If any of them were to change, then the binding would automatically fire again and update the display.

After playing with this a bit, I thought that it would also be interesting if you could select some text with your mouse and have all of the matches for the text highlighted. I created another custom binding that sets an observable based on the currently selected text. So, this could be used to update the search string for the highlightedText binding and highlight any matches for the selected string. This selectedText binding looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//set a value based on the text that a user selects
ko.bindingHandlers.selectedText = {
    init: function(element, valueAccessor, allBindingsAccessor) {
        var value = valueAccessor();
        ko.utils.registerEventHandler(element, 'mouseup', function() {
            var modelValue = valueAccessor();
            //get the selected text
            var selectedText = '';
            if (window.getSelection) {
                selectedText = window.getSelection();
            } else if (document.getSelection) {
                selectedText = document.getSelection();
            } else if (document.selection) {
                selectedText = document.selection.createRange().text;
            }
            //only change if something was selected
            if (selectedText.toString()) {
                if (ko.isWriteableObservable(modelValue)) {
                    modelValue(selectedText.toString());
                }
                else { //handle non-observables
                    var allBindings = allBindingsAccessor();
                    if (allBindings['_ko_property_writers'] && allBindings['_ko_property_writers']['selectedText']) allBindings['_ko_property_writers']['selectedText'](selectedText);
                }
            }
        });
    }
};

The binding works by handling the mouseup event of the element where we retrieve the selected text and properly sets the observable passed to the binding based on the text. It could be made a little less verbose, if there was an easier way to get the selected text across browsers and if it didn’t bother trying to support binding to non-observables.

I thought that maybe these bindings would be useful to someone or at least help to demonstrate how simple it is to create custom bindings that handle these types of behaviors.

Full sample here:

Link to sample on jsFiddle.net

Creating a Smart, Dirty Flag in KnockoutJS

| Comments

Recently, I was thinking about the best way to implement a generic dirty flag in KnockoutJS and had a few thoughts. The basic idea is that when any change is made to an object or possibly the entire view model, we want a boolean to be set. With this flag, we are now able to recognize that we might need to send some or all of our data back to the server.

Implementing a basic dirty flag

In order to determine that a change has been made to our object, we really need to be notified when anything changes. We need to create a subscription to every observable on our object. Conveniently, we have a utility function, ko.toJS, that given a root object will access all of the observable properties. So, if we create a computed observable that executes ko.toJS, then we will automatically set up subscriptions to all of our observables. Then, we can subscribe to this single computed observable and update our flag when it changes.

One issue with this method is that every change made to any of our observables will trigger the computed observable to be re-evaluated, which will run ko.toJS again. This could get expensive, if we are doing this at the view model level. Fortunately, it is easy enough for us to drop our subscriptions after the first change is detected. So, a basic dirty flag might look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ko.dirtyFlag = function (root) {
   var _initialized;

   //one-time dirty flag that gives up its dependencies on first change
   var result = ko.computed(function () {
       if (!_initialized) {
           //just for subscriptions
           ko.toJS(root);

           //next time return true and avoid ko.toJS
           _initialized = true;

           //on initialization this flag is not dirty
           return false;
       }

       //on subsequent changes, flag is now dirty
       return true;
   });

   return result;
};

This function will return a computed observable that starts as false and moves to true on the first change to any observables inside of the “root” object. On the first change, the computed observable will skip running ko.toJS and will be left with no dependencies.

One thing to remember is that computed observables are evaluated for the first time when they are created. So, in order for our dirty flag to subscribe to all of our observables, we need to add it last.

It seems like this approach could be sufficient for many cases. Even putting this flag on an entire view model should be fine, as the subscription to all observables will be dropped as soon as it is dirty.

Adding some smarts to the flag

For my scenario, I wanted a little bit of additional functionality. It would be nice if the dirty flag would set itself back to false if the changes are reverted. Additionally, if I receive updates from the server, then I want to reset the dirty flag to clean after the updates have been applied. Finally, if I add a new item, we should be able to mark it as dirty right away.

There are a couple of challenges that I ran into while implementing these features. First, if the computed observable calls ko.toJS any time after it has been added, then it recursively tries to evaluate itself. Second, if the computed observable depends on itself, then when a change is made it will get into an infinite loop.

I considered a few different ways to solve this issue.

  1. removing the computed observable from its parent prior to calling ko.toJS in the read function and then adding itself back. This works, but it seemed like a little too much code to understand what my property name is on the parent, delete the property, and add it back afterwards.
  2. manipulating the computed observable to pretend that it is not an observable until after ko.toJS runs. I was unable to get this working properly and it required access to properties of KO that are considered private.
  3. writing a new version of ko.toJS that skips some properties.
  4. making the object a function and adding the computed observable to the function object. This means you have to bind to it as dirtyFlag.isDirty(). When ko.toJS runs, it will just see a plain function and ignore it.

I decided to go with the last option, as it seemed to be safe and straightforward. The inconvenience of having to bind to the sub-property is minor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ko.dirtyFlag = function(root, isInitiallyDirty) {
    var result = function() {},
        _initialState = ko.observable(ko.toJSON(root)),
        _isInitiallyDirty = ko.observable(isInitiallyDirty);

    result.isDirty = ko.computed(function() {
        return _isInitiallyDirty() || _initialState() !== ko.toJSON(root);
    });

    result.reset = function() {
        _initialState(ko.toJSON(root));
        _isInitiallyDirty(false);
    };

    return result;
};
  • This version of the flag does not give up its subscriptions after it becomes dirty. It will evaluate each change against the original version of the data to determine if it is still dirty. So, this flag is likely most appropriate for small objects rather than an entire view model.
  • If a new item is added, you can mark the flag as dirty immediately.
  • A reset method is provided to take a new snapshot of the current state. This can be used when applying updates from the server or after saving your data to the server, if you still allow for additional updates. Here is a sample using this flag at an item level:

Link to full sample on jsFiddle.net

Link to sample on jsFiddle.net

Dragging, Dropping, and Sorting With observableArrays

| Comments

Update: take a look at an updated version of this functionality here.

Using features from jQuery UI with KnockoutJS seems to be a fairly common scenario. Frequently, I have seen discussions about using custom bindings to initiate the jQuery UI widgets. There are several libraries of bindings for this purpose discussed in this thread. However, I haven’t seen much information about using some of the interactions like sortable, and droppable with Knockout.

It is easy enough to have a binding that initiates the draggable and sortable behaviors, but I think that this really becomes powerful when you are able to connect it to your view model. After all, once a user sorts some objects, it is not very useful unless you can save the state back to the server. I believe that we are in the best position when our view model is the source of truth.

To make this connection, we can use a custom binding. The jQuery UI interactions provide many events that we can tap into. However, within those events we are always dealing with DOM elements. So, our event handler needs to be able to get back to our data from an element. There are several ways to accomplish this task. For simply sorting a single list, we can use the jQuery Template plugin’s tmplItem function to get back to our data item. To get to the parent of the item, we can use the value that we pass to our binding.

Update: as of Knockout 2.0, there is now an API, ko.dataFor that will work universally.

So, a simple sortableList binding that you can place on the parent of your sortable elements could look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//connect items with observableArrays
  ko.bindingHandlers.sortableList = {
      init: function(element, valueAccessor) {
          var list = valueAccessor();
          $(element).sortable({
              update: function(event, ui) {
                  //retrieve our actual data item
                  var item = ui.item.tmplItem().data;
                  //figure out its new position
                  var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
                  //remove the item and add it back in the right spot
                  if (position >= 0) {
                      list.remove(item);
                      list.splice(position, 0, item);
                  }
              }
          });
      }
  };

Then, you would use the binding on the parent like:

1
<div class="container" data-bind="template: { name: 'taskTmpl', foreach: tasks}, sortableList: tasks"></div>

So, we identify the list from what is passed to the binding. We set up a handler for the update event triggered by jQuery UI when an item is dropped. The actual item in our array is retrieved using the tmplItem function on the element that was dropped. Then, we find the new index of the element and move our data item to the corresponding spot in its array. Now when a user drops an element in a new location, it will be reflected in our observableArray.

Here is a sample:

Link to full sample on jsFiddle.net

If you would want to pass additional options to the sortable function, then you could pass an object to the binding that has both your parent and the options to pass to jQuery UI.

Allowing items to be dropped between arrays

The next feature that I wanted to support was allowing an item to be dropped between multiple arrays. The logic necessary to make this happen is quite similar, but we need to know a little more information when an element is dropped. We need to know the data item, the item’s original parent array, and the item’s new array.

Making this connection with strictly tmplItem and the value passed to the binding was not working out properly for me. I decided to be a bit more explicit about the relationship between the elements and the data on my view model. Instead of using just the sortableList binding, I decided to also attach a sortableItem binding on the children of the elements.

The sortableItem binding only has an init function that uses jQuery’s data method to attach some meta-data to the element. I track the underlying data and the current parent of the item, which are passed to the binding.

1
2
3
4
5
6
7
8
//attach meta-data
ko.bindingHandlers.sortableItem = {
    init: function(element, valueAccessor) {
        var options = valueAccessor();
        $(element).data("sortItem", options.item);
        $(element).data("parentList", options.parentList);
    }
};

Additionally, the sortableList binding was tweaked a little bit to relate the container with its underlying array. Also, the update event handler now will remove the item from the original parent and add it to the new parent. This also works out properly for sorting within the same list, as the original and new parent will just be the same.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//connect items with observableArrays
ko.bindingHandlers.sortableList = {
    init: function(element, valueAccessor, allBindingsAccessor, context) {
        $(element).data("sortList", valueAccessor()); //attach meta-data
        $(element).sortable({
            update: function(event, ui) {
                var item = ui.item.data("sortItem");
                if (item) {
                    //identify parents
                    var originalParent = ui.item.data("parentList");
                    var newParent = ui.item.parent().data("sortList");
                    //figure out its new position
                    var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
                    if (position >= 0) {
                        originalParent.remove(item);
                        newParent.splice(position, 0, item);
                    }
                }
            },
            connectWith: '.container'
        });
    }
};

Here is a sample that allows both sorting and dragging items between arrays:

Link to full sample on jsFiddle.net

The next steps with this binding would probably be to make it a bit more generic and accept additional options to be passed to sortable.

Seems like this would be an easy and useful way to add some nice functionality to a list of items. Being able to connect the changes directly to your view model allows you to persist the new state back to the server without any further logic.