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.
1
|
|
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
|
|
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
|
|
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 |
|
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 |
|
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 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 |
|
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 |
|
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 |
|
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.