Knock Me Out

Thoughts, ideas, and discussion about Knockout.js

Knockout.js 3.1 Released

| Comments

Knockout version 3.1 is available now! This was a nice incremental release with a few new features, some performance enhancements, and a number of bug fixes. For a complete list of changes, check out the release notes.

Here are some of my favorite changes:

rateLimit extender

There is a new rateLimit extender that handles throttle and debounce scenarios (docs here). Some notes about this functionality:

  • The throttle extender is deprecated. There are some slight differences between rateLimit and throttle described here.
  • The rateLimit extender returns the original value rather than a new computed like the throttle extender. This saves the overhead of an additional computed and better handles scenarios where the original value has already been extended in other ways (perhaps the original had an isValid sub-observable, which became inaccessible when the throttle extender created a new computed to stand in front of it.
  • The rateLimit extender works properly with observables, computeds, and observableArrays.
  • It supports a method option that by default is set to notifyAtFixedRate which corresponds to a throttling strategy. For a debounce scenario, the method can be set to notifyWhenChangesStop.

For example, suppose that we define three observableArrays.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//when updated will notify subscribers immediately
this.normal = ko.observableArray();

//when updated will notify subscribers no more than every 500ms
this.throttled = ko.observableArray().extend({
    rateLimit: 500
});

//when updated will notify subscribers when changes have stopped for 500 ms
this.debounced = ko.observableArray().extend({
    rateLimit: {
        timeout: 500,
        method: "notifyWhenChangesStop"
    }
});

In the jsFiddle below, when you click the “Start” button, a new item will be pushed to each observableArray every 100ms for 100 iterations to demonstrate the behavior under each option.

The rateLimit extender will be a key tool going forward. I feel that after upgrading to 3.1 it would be worthwhile to review existing uses of the throttle extender in an app and update to rateLimit.

Link to full sample on jsFiddle.net

valueAllowUnset option

There is now a valueAllowUnset option for scenarios where the value does not match what is contained in the options:

  • With this option set to true, Knockout does not force the value to match an existing option.
  • The selection will be set to an empty option in the case of a mismatch, but the value is not overwritten.
  • This is very useful in scenarios where options are lazily loaded and there is an existing value.
1
`<select data-bind="value: selectedCategory, options: categories, valueAllowUnset: true"></select>`
In this example, `selectedCategory` may be loaded with a value from the database and then when an editor is shown `categories` are loaded via an AJAX request. Without the `valueAllowUnset` option, Knockout would have recognized that the value of `selectedCategory` did not match an available option and tried to set it to the first option or undefined if there are no options. Without taking care to cache the original value, `selectedCategory` would have lost its value.

ko.computedContext

  • You can now access the dependency count and whether it is the first evaluation within a computed. This could be useful in scenarios where you know that the function will never be called again (no dependencies) or to run special logic (or avoid logic) when it is the first evaluation. By using ko.computedContext while evaluating a computed, you have access to the isInitial and getDependenciesCount functions:
1
2
3
4
5
6
7
8
9
10
11
12
13
this.saveFlag = ko.computed(function() {
    var cleanData = ko.toJS(this);

    //don't save the data on the initial evaluation
    if (!ko.computedContext.isInitial()) {
      //actually save the data
    }

    //recognize that we have no dependencies
    if (!ko.computedContext.getDependenciesCount()) {
       //we might want to do some cleanup, as this computed will never be re-evaluated again
    }
}, this);

array methods pass index

The array utility methods now pass the array index as the second argument to the callbacks (in addition to the array item as the first argument).

1
2
3
4
ko.utils.arrayFilter(this.pages(), function(page, index) {
    //include any summary pages, besides the very first page
    return index > 0 && page.type === "summary";
});

Use __proto__ for base types

In browsers that support it, the base types (ko.subscribable, ko.observable, ko.computed, ko.observableArray) now use the fn objects as their prototype rather than copying functions from the fn objects to the result (since the result itself is a function, in older browsers it is not possible to set the prototype for it).

Dependency tracking performance enhancement

Dependencies are tracked in objects with unique ids, rather than in an array on each subscribable. This eliminates the need to loop internally to find an existing dependency, which has caused long-running script errors in older browsers when there were a large number of dependencies for a single observable/computed.

Look for jQuery later

Knockout looks for jQuery when applyBindings is called rather than when KO is loaded. This helps eliminate an issue with the order that KO and jQuery are loaded and in an AMD scenario you would not have to add jQuery as a dependency to KO in the shim configuration to have KO take advantage of jQuery.

There are many additional fixes listed in the release notes here. Please log any issues related to 3.1 to GitHub. Michael Best again did a tremendous job with the bulk of the changes along with Steve Sanderson as well as a number of community contributors.

Knockout.js 3.0 Highlights

| Comments

Knockout version 3.0 is now available. Here is a list of links with all of the info:

  • Release on Github - look at the 3.0 Beta and 3.0 RC releases to see a list of changes.
  • Upgrade notes - a list of potential breaking changes.
  • Blog post on 3.0 beta - Steve Sanderson’s post describing the features in 3.0 beta (bottom of post).
  • Blog post on 3.0 RC - another post by Steve on some additional features added in 3.0 RC.
  • knockout.punches - a great plugin by Michael Best that utilizes many of the new extensibility points added in KO 3.0. Michael was the driving force behind the major changes in 3.0 and continues to do amazing work on Knockout core.

I put together a short screencast this morning with some highlights of the features and changes in 3.0:

The new extensibility points in KO 3.0 really open up a ton of interesting options for customizing the way that Knockout can be used. This is definitely an exciting and solid release!

devLink 2013 - Knockout.js Tips and Tricks

| Comments

Last week, I was fortunate enough to attend devLink in Chattanooga, Tennessee for the first time. I had the chance to see many of my appendTo co-workers in person and meet a number of other folks that I had only corresponded with on-line. I attended many great sessions and overall felt that it was a great experience.

I presented a session on Knockout tips and tricks. These were some things that I felt would be useful to a developer after they have the basics of Knockout done. The session included these ten rounds of tips:

  1. Dynamic Templating
  2. Controlling “this”
  3. Throttling
  4. Subscriptions
  5. Custom Bindings
  6. Extensions
  7. Computed options
  8. Binding Providers
  9. Debugging Tips
  10. Code Smells

For this type of talk with lots of live coding, it is challenging to share the slides. So, I recorded a screencast of the presentation. Click here to watch in HD or to download it.

devLink 2013 - KnockoutJS Tips and Tricks - Ryan Niemeyer

I am definitely hoping to return to devLink next year and would be happy to hear any feedback or questions about this presentation. Photo by @jcreamer898.

Knockout.js 2.3.0 and 3.0.0 Beta Are Out

| Comments

Just a brief note that Knockout 2.3.0 is now available. Additionally, there is a beta release of Knockout 3.0.0 available here. Steve Sanderson has a great post highlighting the features/fixes in 2.3.0 and the changes in 3.0.0 beta. I look forward to writing more about 3.0 changes in the coming months.

2.3.0 contains a ton of fixes and improvements. Here is a copy of the 2.3.0 release notes:

Features:

  • hasfocus renamed to hasFocus (hasfocus will still continue to work as well)
  • name parameter of template can accept an observable
  • ko.unwrap added as substitute for ko.utils.unwrapObservable
  • options binding uses same technique as foreach to avoid unnecessary re-rendering
  • optionsAfterRender callback added to allow for custom processing of the added options
  • optionsCaption will display a blank caption if the value is an empty string

Bugs fixed:

  • hasfocus: requires two Tab presses to blur (and related issues) in Chrome; throws error in IE9 on initial binding
  • Error when you try to inline knockout because of </script> string
  • If first node in string template has a binding, the bindings in the template won’t get updated
  • selectedOptions doesn’t update non-observable properties
  • css won’t accept class names with special characters
  • Applying binding twice to the same nodes fails in strange ways (now it fails with an obvious error message)
  • Two-way bindings overwrite a read-only computed property with a plain value
  • Empty template throws an error with jQuery 1.8+
  • observableArray can be initialized with non-array values
  • Event binding init fails if Object.prototype is extended (fixed all for...in loops)
  • Calling ko.isSubscribable with null or undefined causes an error
  • Binding to null or undefined causes an error
  • Memory leak in IE 7, 8 and 9 from event handlers
  • options binding causes error in IE8+ in compatibility mode
  • value binding on select doesn’t match null with caption option
  • Invalid element in string template can cause a binding error
  • Conditionally included select gets close to zero width in IE7-8
  • ko.toJS treats Number, String and Boolean instances as objects
  • value binding misses some updates (1 -> "+1")
  • Exception in ko.computed read function kills the computed
  • Error if spaces around = in data-bind attribute in string template

Maintenance:

  • Port tests to Jasmine
  • Support Node.js
  • Remove build output files and Windows build scripts
  • Build script runs tests using build output (using PhantomJS and Node.js)
  • Use faster string trim function
  • Add automated multi-browser testing using Testling-CI

Knockout.js Troubleshooting Strategies

| Comments

This post contains a number of tips, tricks, and strategies that I have used over the last couple of years to debug Knockout applications. Feel free to share some of your own tips in the comments or suggest other areas that you would like to see discussed.

Binding issues

The most common errors encountered in Knockout typically center around incorrect or invalid bindings. Here are several different ways that you can diagnose and understand the context of your issue.

The classic “pre” tag

One of the main debugging tasks is to determine what data is being used to bind against a certain element. The quick-and-dirty way that I have traditionally accompished this is by creating a “pre” tag that outputs the particular data context that is in question.

Knockout includes a helper function called ko.toJSON, which first creates a clean JavaScript object that has all observables/computeds turned into plain values and then uses JSON.stringify to turn it into a JSON string. You would apply this to a “pre” tag like:

1
    <pre data-bind="text: ko.toJSON($data, null, 2)"></pre>

As of KO 2.1, the second and third arguments are passed through to JSON.stringify with the third argument controlling the indentation to produce nicely formatted output. Prior to KO 2.1, you could still do this by using JSON.stringify(ko.toJS($data), null, 2). You can use this technique to look at specific properties or use the special context variables like $data, $parent, or $root. This will give you output like:

{
  "title": "Some items",
  "items": [
    {
      "description": "one",
      "price": 0
    },
    {
      "description": "two",
      "price": 0
    },
    {
      "description": "three",
      "price": 0
    }
  ],
  "titleUpper": "SOME ITEMS"
}

Lazy? Then just console.log it

Does that “pre” tag look like too much work? An even easier and more direct solution is to simply put a console.log in your binding string. The binding string is parsed into a JavaScript object, so code that you place in it will be executed. In addition, you can also put bindings that don’t exist into your binding string. This is because bindings can be used as options in other bindings (think optionsText or optionsValue), so KO does not throw an exception when it finds an undefined binding handler. This means that you can simply do:

1
    <input data-bind="blah: console.log($data), value: description" />

The block above will output your data to the console just before encountering a typo in the description property. You may even want to wrap your data in a call to ko.toJS or ko.toJSON depending on how you want the output to appear in the console.

Update: in KO 3.0, a binding with no actual handler, will no longer get executed. You would need to use an actual binding like the custom one described below or even something harmless like the built-in uniqueName binding.

Extensions / bookmarklets

There are several great community-created solutions for doing this type of investigation as well:

  • Knockout Context Debugger Chrome extension - adds a pane to the Chrome dev tools that allows you to see the context associated with a specific element. Update: the latest version adds some nice tracing functionality as well. Created by Tim Stuyckens.
  • Knockout Glimpse plugin - Glimpse is a powerful debugging tool for ASP.NET. Aaron Powell created an excellent Knockout plugin for it.
  • Bowtie - a bookmarklet by Max Pollack that lets you inspect the context associated with bindings and add watch values. Note - it does assume that the page includes jQuery.
  • Knockout-debug - a bookmarklet by James Smith that gives a nice display of your view model.

Performance testing - how often is a binding firing?

Sometimes an interesting thing to explore/investigate is how often a certain binding is being triggered and with what values. I like to use a custom binding for this purpose.

1
2
3
4
5
6
7
8
9
10
11
12
13
    ko.bindingHandlers.logger = {
        update: function(element, valueAccessor, allBindings) {
            //store a counter with this element
            var count = ko.utils.domData.get(element, "_ko_logger") || 0,
                data = ko.toJS(valueAccessor() || allBindings());

            ko.utils.domData.set(element, "_ko_logger", ++count);

            if (window.console && console.log) {
                console.log(count, element, data);
            }
        }
    };

This will show you a count of how many times the bindings on this element have been fired. You can either pass in a value to log or it will log the values provided to all bindings on that element. You may want to include a timestamp as well, depending on your needs. There are many times where this will open your eyes to a lot of unnecessary work happening in your UI. It may be from pushing again and again to an observableArray or maybe a case where throttling would be appropriate. For example, you might place this on an element like:

1
    <input data-bind="logger: description, value: description, enable: isEditable" />

and expect output like:

 1 <input…/> “Robert”
 2 <input…/> “Bob”
 3 <input…/> “Bobby”
 4 <input…/> “Rob”
 

Just a note that in the KO 3.0 (almost in beta), each binding on an element will be fired independently, as opposed to now where all bindings fire together. When KO 3.0 is released, you would want to use a binding like this with that in mind and likely only pass the specific dependencies that you are interested in logging.

Undefined properties

Another common source of binding issues is undefined properties. Here is a quick tip for handling scenarios where a property is missing and it will not be added later. This is not specifically a debugging tip, but it can help you avoid potential binding issues without much fuss.

For example, this would cause an error (if myMissingProperty is missing):

1
    <span data-bind="text: myMissingProperty"></span>

However, this will bind properly against undefined:

1
    <span data-bind="text: $data.myMissingProperty"></span>

In JavaScript it is an error to attempt to retrieve the value of a variable that is not defined, but it is fine to access an undefined property off of another object. So, while myMissingProperty and $data.myMissingProperty are equivalent, you can avoid the “variable is not defined” errors by referencing the property off of its parent object ($data).

Catching exceptions using a custom binding provider

Errors in bindings are inevitable in a Knockout application. Many of the techniques described in the first section can help you understand the context around your binding. There is another option that can also help you track and log problems in your bindings. A custom binding provider provides a nice extensibility point for trapping these exceptions.

For example, a simple “wrapper” binding provider might look like:

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

        ko.bindingProvider.instance = {
            nodeHasBindings: existing.nodeHasBindings,
            getBindings: function(node, bindingContext) {
                var bindings;
                try {
                   bindings = existing.getBindings(node, bindingContext);
                }
                catch (ex) {
                   if (window.console && console.log) {
                       console.log("binding error", ex.message, node, bindingContext);
                   }
                }

                return bindings;
            }
        };

    })();

Now, binding exceptions will be caught and logged with the error message, the element, and the binding context (contains $data, $parent, etc.). A binding provider can also be a useful tool to log and diagnose the amount of times that bindings are being hit.

View Model issues

Binding issues seem to be the most common source of errors in Knockout, but there are still errors/bugs on the view model side as well. Here are a few ways to instrument and control your view model code.

Manul subscriptions for logging

A basic technique to help understand how things are being triggered in your view model is to create manual subscriptions to your observables or computeds to log the values. These subscriptions do not create dependencies of their own, so you can freely log individual values or the entire object. You can do this for observables, computeds, and observableArrays.

1
2
3
4
5
6
7
    this.firstName = ko.observable();
    this.firstName.triggeredCount = 0;
    this.firstName.subscribe(function(newValue) {
        if (window.console && console.log) {
            console.log(++this.triggeredCount, "firstName triggered with new value", newValue);
        }
    });

You could format the message to include whatever information is relevant to you. It would also be easy enough to create an extension to add this type of tracking to any observable/computed:

1
2
3
4
5
6
7
8
9
10
    ko.subscribable.fn.logIt = function(name) {
        this.triggeredCount = 0;
        this.subscribe(function(newValue) {
            if (window.console && console.log) {
                console.log(++this.triggeredCount, name + " triggered with new value", newValue);
            }
        }, this);

        return this;
    };

Since the extension returns this, you can chain it onto an observable/computed during creation like:

1
   this.firstName = ko.observable(first).logIt(this.username + " firstName");

In this case, I chose to include the username to ensure that it is outputting a unique value in the case that I have a collection of objects that each have a firstName observable. The output would look something like:

1 "bob1234 firstName triggered with new value" "Robert"
2 "bob1234 firstName triggered with new value" "Bob"
3 "bob1234 firstName triggered with new value" "Bobby"
4 "bob1234 firstName triggered with new value" "Rob"

The value of “this”

Issues with the value of this is one of the most common challenges encountered when starting out with Knockout. Whenever you are binding to an event (click / event binding) and are using a function off of another context (like $parent or $root), you will generally need to worry about the context, as Knockout executes these handlers using the current data as the value of this.

There are several ways to ensure that you have the appropriate context:

1- you can use .bind to create a new function that is always bound to a specific context from within your view model. If the browser does not support bind natively, then Knockout adds a shim for it.

1
2
3
  this.removeItem = function(item) {
      this.items.remove(item);
  }.bind(this);

or if your function lives on the prototype, you would have to create a bound version on each instance (not ideal) like:

1
  this.removeItem = this.removeItem.bind(this);

2- you can create a variable in your view model that corresponds to the current instance (var self = this;) and always use self rather than this in your handlers. This technique is convenient, but does not lend itself to placing functions on a prototype object or adding functions through mix-ins, as these functions need to access the instance through the value of this (unless bound like in #1).

1
2
3
4
5
 var self = this;

  this.removeItem = function(item) {
     self.items.remove(item);
  };

3- you can even use .bind in the binding string, depending on how comfortable you are with seeing it in your markup. This would look like:

1
    <button data-bind="click: $parent.removeItem.bind($parent)">Remove Item</button>

Similarly, you could create a simple custom binding that wraps the event binding and takes in a handler and a context like data-bind="clickWithContext: { action: $parent.removeItem, context: $parent }".

4- the way that I personally handle this now is to use my delegated events plugin, which will do event delegation and as a by-product is able to automatically call the method off of the object that owns it. This also makes it convenient/practical to place your functions on the prototype of your object. With this plugin, the above sample would simply look like:

1
    <button data-click="removeItem">Remove Item</button>

I have found that this removes the vast majority of the context binding concerns that I have in my viewmodel code.

Using the console

One of my favorite ways to debug/investigate a Knockout app is to use the browser’s debugging console. If your view model is well written, you can generally control your entire application from the console. Recently, I ran a data conversion involving hundreds of records from the debugging console by loading the appropriate data, looping through each record to manipulate any observables that needed updating, and saving each record back to the database. This may not always be the best idea, but it was the most practical and efficient way for me to accomplish the conversion for my scenario.

Accessing your data

The first step is getting a reference to your data from the console. If your app is exposed globally, perhaps under a specific namespace, then you are already good to go. However, even if you call ko.applyBindings without your view model being available globally, you can still easily get at your data. Knockout includes ko.dataFor and ko.contextFor helper methods that given an element tell you the data/context that was available to the element during binding. For example, to get your overall view model, you may start with something like this in the console:

1
    var data = ko.dataFor(document.body);

Now you have access to your overall view model (if you simply called ko.applyBindings without specifying a specific root element). With access to that view model, you can call methods, set observable values, and log/inspect specific objects.

Making logging a bit smarter

Anyone that has done debugging in Knockout has likely done a console.log on an observable or computed directly at some point and found some less than useful output like:

1
function d(){if(0<arguments.length){if(!d.equalityComparer||!d.equalityComparer(c,arguments[0]))d.H(),c=arguments[0],d.G();return this}b.r.Wa(d);return c}

One way to improve this output is to add a toString function to the various types. I like to show the name of the type (like observable or computed) and the current value. This helps you quickly understand that it is indeed a KO object, which type it is, and the latest value. This might look like:

1
2
3
4
5
6
7
    ko.observable.fn.toString = function() {
        return "observable: " + ko.toJSON(this(), null, 2);
    };

    ko.computed.fn.toString = function() {
        return "computed: " + ko.toJSON(this(), null, 2);
    };

The output in the Chrome console would now look like:

observable: "Bob"
computed: "Bob Smith"
observable: "Jones"
computed: "Bob Jones"

Note that not all browsers respect the toString function, but it certainly helps with Chrome. Also, observableArrays are a bit harder to handle. Chrome sees that the observableArray (function) has both a length and a splice method and assumes that it is an array. Adding a toString function is not effective in this case, unless you delete the splice function from observableArrays.

Final notes

  • Debugging Knockout core - When you encounter exceptions in your Knockout application, I would not recommend trying to debug the Knockout core library code as your first choice. It is almost always more effective and efficient to isolate the origin of the issue in your application code first and try to simplify the reproduction of that issue. In my experience, the main situation where stepping into Knockout core is potentially a good choice is if you are truly seeing inconsistent behavior between browsers. Knockout core can be challenging to step through and follow, so if you do find yourself debugging into the core library, a good place to start is within the binding handlers themselves.

  • Be careful with logging - if you add logging or instrumentation to your code, be mindful of how this logging may contribute to the dependencies of your computeds/bindings. For example, if you logged ko.toJSON($root) in a computed you would gain a dependency on all observables in the structure. Additionally, if you have a large view model, be aware of the potential performance impact of frequently converting large object graphs to JSON and logging or displaying the result. Throttling may help in that scenario.

  • console.log format - the console.log calls in the snippets are just samples. I understand that some browers don’t support passing multiple arguments to console.log and some do not let you navigate object hierarchies. If you need to do this logging in a variety of browers, then make sure that you build suitable output. The normal case where I am doing logging is temporarily in Chrome during development.