Knock Me Out

Thoughts, ideas, and discussion about Knockout.js

Quick Tip: Telling Knockout to Skip Binding Part of a Page

| Comments

Recently, I worked with several people on questions related to binding multiple view models in a single page. One common approach is to bind a view model to a particular root element using a call like ko.applyBindings(vm, containerNode);. However, a limitation with this approach is that when binding multiple view models, none of the container elements can overlap. This means that you could not bind one view model nested inside of another.

One way to address this issue is to create a top level view model that contains your “sub” view models and then call ko.applyBindings on the entire page with the overall view model:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var profileModel = {
    first: ko.observable("Bob"),
    last: ko.observable("Smith")
};

var shellModel = {
    header: ko.observable("Administration"),
    sections: ["profile", "settings", "notifications"],
    selectedSection: ko.observable()
};

//the overall view model
var viewModel = {
    shell: shellModel,
    profile: profileModel
};

ko.applyBindings(viewModel);

Now in the view, you can use the with binding along with $root to bind a nested view model:

1
2
3
4
5
6
7
<div data-bind="with: shell">
    <h2 data-bind="text: header"></h2>
    <div data-bind="with: $root.profile">
        ...
    </div>
    ...
</div>

This technique is nice, because you only have to make a single ko.applyBindings call and you can use $root or $parent/$parents to access data at any time from another view model. However, based on a desire to maintain modular code and to control how and when elements are bound, it is often not convenient or practical to build a top level view model.

With Knockout 2.0, there is a simple alternative that can provide for greater flexibility. Bindings are now able to return a flag called controlsDescendantBindings in their init function to indicate that the current binding loop should not try to bind this element’s children. This flag is used by the template and control-flow bindings (wrappers to the template binding), as they will handle binding their own children with an appropriate data context.

For our scenario, we can take advantage of this flag and simply tell Knockout to leave a certain section alone by using a simple custom binding:

1
2
3
4
5
ko.bindingHandlers.stopBinding = {
    init: function() {
        return { controlsDescendantBindings: true };
    }
};

Now, we can bind our “shell” model to the entire page and bind our “profile” model to the specific container:

1
2
3
4
5
6
7
8
9
10
11
12
13
var profileModel = {
    first: ko.observable("Bob"),
    last: ko.observable("Smith")
};

var shellModel = {
    header: ko.observable("Administration"),
    sections: ["profile", "settings", "notifications"],
    selectedSection: ko.observable()
};

ko.applyBindings(shellModel);
ko.applyBindings(profileModel, document.getElementById("profile"));

In our view, we can now use the simple stopBinding custom binding around our inner container element:

1
2
3
4
5
6
7
8
9
10
<div>
    <h2 data-bind="text: header"></h2>
    <div data-bind="stopBinding: true">
        <div id="profile">
            <input data-bind="value: first" />
            <input data-bind="value: last" />
        </div>
    </div>
    ...
</div>

Adding the extra div to hold our stopBinding binding may not cause our app any problems, but if it does then in KO 2.1 we can now create containerless custom bindings by adding our binding to ko.virtualElements.allowedBindings.

1
2
3
4
5
6
7
ko.bindingHandlers.stopBinding = {
    init: function() {
        return { controlsDescendantBindings: true };
    }
};

ko.virtualElements.allowedBindings.stopBinding = true;

and finally we can clean up our view to look like:

1
2
3
4
5
6
7
8
9
<div>
    <h2 data-bind="text: header"></h2>
    <!-- ko stopBinding: true -->
    <div id="profile">
        <input data-bind="value: first" />
        <input data-bind="value: last" />
    </div>
    <!-- /ko -->
</div>

With this simple binding, we can now compose pages with multiple view models without the worry of conflicting/overlapping bindings.

Here is a live sample:

Link to full sample on jsFiddle.net

Knockout 2.1 Is Out

| Comments

Knockout.JS 2.1 is now available! Minified and debug versions available here. This release focused mainly on performance improvements and bug fixes after the 2.0 release.

Here are some highlights:

  1. CommonJS and AMD support
  2. Great performance improvements to templating - use cloned DOM nodes when possible rather than parsing strings into elements.
  3. Support for custom container-less bindings
  4. Ability to extend the binding context in a custom binding
  5. Computed observables no longer can recursively evaluate themselves (I think that has happened to all of us a few times!)
  6. $index is available in foreach scenarios from the binding context. There is no longer a need for workarounds like storing an index as a property of your data items. Note that $index is an observable, so it needs to be reference using $index() in expressions.
  7. $parentContext is available from the binding context as well. While, $parent contains the actual data from one scope level up, $parentContext contains the actual binding context ($data, $parent, $parents, $root, etc.) of the parent.
  8. ko.isComputed is a helper function that was added to determine if a value is a computed observable or not.
  9. ko.toJSON now passes its arguments on to JSON.stringify after calling ko.toJS. This makes creating a handy “debug” section even easier <pre data-bind="text: ko.toJSON($root, null, 2)"></pre> and this can be used to include only certain properties in the converted JSON like ko.toJSON(person, ['firstName', 'lastName']). This seems even more flexible in many cases than the approach that I mentioned here.
  10. Better support for using a single instance of KO between documents - you can now pass an instance of KO between documents and have bindings work properly across the documents. Previously the computed observables associated with bindings would dispose themselves when outside of the original document as they believed they were no longer part of the DOM.
  11. Numerous other bug and performance fixes

For this release Michael Best joined the core team and drove the majority of the changes with Steve. Michael has numerous other exciting performance enhancements in his fork that will be evaluated for possible inclusion (in a non-breaking fashion) in the core. He has really helped tremendously in moving the project forward.

Please report any issues found here.

Giving the Blog a Facelift

| Comments

I just finished the process of moving this blog from Blogger to an Octopress site that uses Jekyll to generate a static blog. I am excited to have better control over the look and feel of the blog and to move to a git-based workflow using markdown files for posts.

Here are a few of the steps that I took to make this move:

  • Converted Blogger site to use disqus for comments
  • Imported old comments using the import tool from disqus
  • Imported blog posts into Octopress using the script here to at least get some basic structure and permalinks.
  • At that point, I scrapped all of the HTML for each post and recreated each in markdown. It was a painful and time consuming process, but the posts will hopefully now look consistent and will be easier to edit/update in the future.
  • Made updates/change to the layout and look of the default template.
  • Created an app in Heroku, pushed the site to it, and added the custom domain.
  • Pointed the domain to Heroku.
  • Updated the Feedburner feed to use the new RSS.

I was never too excited about the look, feel, and process of updating the old site. I was mostly focused on getting content out there and a Blogger blog was an easy way to get started. I am excited to continue tweaking the site and using git and markdown for the posts. Hopefully, I will find it easier to make more frequent posts and to update older posts as Knockout changes.

Knockout.js Performance Gotcha #2 - Manipulating observableArrays

| Comments

Most Knockout.js applications tend to deal with collections of objects that are stored in one or more observableArrays. When dealing with observableArrays, it is helpful to understand exactly how they work to make sure that you don’t hit a performance issue.

An observableArray is simply an observable with some additional methods added to deal with common array manipulation tasks. These functions get a reference to the underlying array, perform an action on it, then notify subscribers that the array has changed (in KO 2.0, there is now an additional notification prior to the change as well).

For example, doing a push on an observableArray essentially does this:

1
2
3
4
5
6
7
ko.observableArray.fn.push = function () {
    var underlyingArray = this();
    this.valueWillMutate();
    var result = underlyingArray.push.apply(underlyingArray, arguments);
    this.valueHasMutated();
    return result;
};

Pushing items to an observableArray

Let’s consider a common scenario where we have a collection of objects along with a computed observable to track a filtered array of those objects.

1
2
3
4
5
6
7
8
9
10
11
12
13
var ViewModel = function() {
    this.items = ko.observableArray([
        new Item("Task One", "high"),
        new Item("Task Two", "normal"),
        new Item("Task Three", "high")
    ]);

    this.highPriorityItems = ko.computed(function() {
        return ko.utils.arrayFilter(this.items(), function(item) {
            return item.priority() === "high";
        });
    };
};

Now, say we want to add additional data from the server to the observableArray of items. It is easy enough to loop through the new data and push each mapped item to our observableArray.

1
2
3
4
5
6
7
this.addNewDataBad = function(newData) {
    var item;
    for (var i = 0, j = newData.length; i < j; i++) {
        item = newData[i];
        self.items.push(new Item(item.name, item.priority));
    }
};

Consider what happens though each time that we call .push(). The item is added to our underlying array and any subscribers are notified of the change. Each time that we push, our highPriorityItems filter code will run again. Additionally, if we are binding our UI to the items observableArray, then the template binding has to do work each time to determine that only the one new item was added.

A better pattern is to get a reference to our underlying array, push to it, then call .valueHasMutated(). Now, our subscribers will only receive one notification indicating that the array has changed.

1
2
3
4
5
6
7
8
this.addNewDataGood = function(newData) {
    var item, underlyingArray = self.items();
    for (var i = 0, j = newData.length; i < j; i++) {
        item = newData[i];
        underlyingArray.push(new Item(item.name, item.priority));
    }
    self.items.valueHasMutated();
};

This can be simplified down to:

1
2
3
4
5
6
7
this.addNewData = function(newData) {
    var newItems = ko.utils.arrayMap(newData, function(item) {
       return new Item(item.name, item.priority);
    });
    //take advantage of push accepting variable arguments
    self.items.push.apply(self.items, newItems);
};

Here is a jsFiddle that tracks the number of re-evaluations for each method: http://jsfiddle.net/rniemeyer/NCpxm/.

Clearing or replacing the contents of an observableArray

Another common scenario is when you want to completely replace the contents of an observableArray. This may be to empty the array or to set it to a new set of items. There is no need to loop through the array and operate on it. The most efficient operation is to simply set it to a new value:

1
2
3
4
5
//clear the observableArray
this.items([]);

//replace the contents of the observableArray
this.otherItems(newData);

Minimizing the amount of notifications to subscribers from observableArrays is an easy way to avoid a potential performance issue. Typically, this can be done by performing your operations on the underling array and then triggering notifications. In other cases, replacing the observableArray’s value with a completely new value is a good choice that results in a single notification.

Knockout.js Performance Gotcha #1 - If/with Bindings Are Not Always Cheap

| Comments

update: Knockout 2.2 has addressed the first issue in this post. Now the if and ifnot binding will not re-render their contents unless the value changes between truthy and falsy.

A recent StackOverflow question prompted me to start a series of short posts with some basic performance tips/gotchas for Knockout.js. In smaller applications, many of these items would go unnoticed, but as a project becomes more complex, it is important to understand the amount of work that Knockout is doing to manage your UI.

The control flow bindings introduced in Knockout 2.0 are really just simple wrappers to the template binding. They use the children of the element as their “template” (what we call anonymous templates) and will re-render the contents whenever triggered. If you are not careful, the if and with bindings may be causing re-renders much more often than you might realize.

Problem: if binding causing frequent re-renders

A common scenario where you might use the if binding is when you want to display a section only when an observableArray actually contains items. Something like:

1
2
3
4
5
6
<!-- ko if: items().length -->
<h2>Items</h2>
<ul data-bind="foreach: items">
    <li data-bind="text: name"></li>
</ul>
<!-- /ko -->

The problem in this case, is that the if binding depends on the items observableArray and re-evaluates every time that it is updated. Each time that it is triggered and has a truthy value, it will actually re-render its “template” again, as you can see here.

Solutions

One choice to avoid these re-renders would be to instead use the visible binding on a container element around our section or on the individual elements, as shown here.

If we prefer to use the if binding in this case, then we need to make sure that it is only triggered when the number of items in our array moves between 0 and 1. One might think that a computed observable holding the length would be a good choice. However, one important thing to note is that currently computed observables will always notify subscribers when re-evaluated, even if their value ends up being the same.

However, this is not true for observables. Notifications are suppressed when you write a value that is identical to the previous value (this behavior can be overridden). So, one option is to subscribe to changes to our observableArray and populate an observable based on the length.

1
2
3
4
5
6
7
this.items = ko.observableArray();

this.hasItems = ko.observable(false);

this.items.subscribe(function(newValue) {
    this.hasItems(newValue && newValue.length ? true : false);
}, this);

Now, we can bind against hasItems and it will only notify subscribers when changing between true and false based on the length of the array. You can here see that each item is no longer re-rendered on every add.

You could even extend observableArrays to add a function that would attach this hasItems property as a sub-observable and create the subscription to keep it updated. This helps to keep the view model a bit cleaner and allows you to easily reuse the code.

It might look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ko.observableArray.fn.trackHasItems = function() {
    //create a sub-observable
    this.hasItems = ko.observable();

    //update it when the observableArray is updated
    this.subscribe(function(newValue) {
        this.hasItems(newValue && newValue.length ? true : false);
    }, this);

    //trigger change to initialize the value
    this.valueHasMutated();

    //support chaining by returning the array
    return this;
};

Here is a sample of how to use this function.

Problem: with binding causes entire area to re-render

The with binding is very handy for binding against nested objects/models and helps to keep your data-bind attributes from becoming too verbose. It is important, however, to understand that the with binding will re-render the entire section whenever its bound value changes.

For example, we might want to render a selected item like:

1
2
3
4
5
<div data-bind="with: selectedItem">
    <h2>Selected</h2>
    <input data-bind="value: name" />
    <input data-bind="value: price" />
</div>

The children of the main div become our template. Whenever selectedItem changes, the contents are re-rendered completely. So, rather than just binding the inputs against the new data, the entire section is replaced with new elements, as shown here. In this case, it won’t cause any issues, but if your markup is complex and/or has events attached outside of Knockout, then you can run into issues when the elements are removed and replaced by the original template.

Solutions

If performance does end up becoming an issue, then we could consider just binding directly to the observables on the selected item. If the selected item might be null, then you would need to make sure that the binding does not fail looking for properties on a null object, as show in this sample.

Another choice would be to use the with binding in smaller, targeted areas. We can make use of the containerless control-flow bindings to wrap more specific sets of elements, as shown here.

Finally, for cases where you want to bind against a nested object that won’t change (non-observable), take a look at the simple withlight binding developed by Michael Best. This will switch the context to your nested object on initialization, but is not designed to bind against an observable that will be changed.

Conclusion

The control-flow bindings are certainly handy, but when performance matters, it helps to understand a little bit about how they are implemented. In cases where you want to avoid re-rendering entire sections of your UI, it takes a little extra work to ensure that this is not happening too frequently.