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
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
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.
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).
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.
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
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
1 2 3 4 5 6 7 8 9
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
1 2 3 4
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
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
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
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
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
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
In addition to the those operations, there are several other methods added for common operations. These include
indexOf (which I always forget).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Note: destroy/destroyAll work like replace/replaceAll, 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
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
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.