Beginners don’t need to know about this, but more advanced developers will want to know why we keep making all these claims about KO automatically tracking dependencies and updating the right parts of the UI…
It’s actually very simple and rather lovely. The tracking algorithm goes like this:
So, Knockout doesn’t just detect dependencies the first time the evaluator runs - it redetects them every time. This means, for example, that the dependencies can vary dynamically: dependency A could determine whether the computed observable also depend on B or C. Then, it will only be re-evaluated when either A or your current choice of B or C changes. You don’t have to declare dependencies: they’re determined at runtime from the code’s execution.
The other neat trick is that declarative bindings are simply implemented as computed observables. So, if a binding reads the value of an observable, that binding becomes dependent on that observable, which causes that binding to be re-evaluated if the observable changes.
Pure computed observables work slightly differently. For more details, see the documentation for pure computed observables.
Knockout’s automatic dependency tracking normally does exactly what you want. But you might sometimes need to control which observables will update your computed observable, especially if the computed observable performs some sort of action, such as making an Ajax request. The peek function lets you access an observable or computed observable without creating a dependency.
In the example below, a computed observable is used to reload an observable named currentPageData using Ajax with data from two other observable properties. The computed observable will update whenever pageIndex changes, but it ignores changes to selectedItem because it is accessed using peek. In this case, the user might want to use the current value of selectedItem only for tracking purposes when a new set of data is loaded.
ko.computed(function() { var params = { page: this.pageIndex(), selected: this.selectedItem.peek() }; $.getJSON('/Some/Json/Service', params, this.currentPageData); }, this);
Note: If you just want to prevent a computed observable from updating too often, see the rateLimit extender.
The ko.ignoreDependencies function is available for scenarios where you want to execute code within a computed that should not contribute to that computed’s dependencies. This is often useful in a custom binding when you want to call code that may access observables, but you do not want to re-trigger the binding based on changes to those observables.
ko.ignoreDependencies( callback, callbackTarget, callbackArgs );
Example:
ko.bindingHandlers.myBinding = { update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { var options = ko.unwrap(valueAccessor()); var value = ko.unwrap(options.value); var afterUpdateHandler = options.afterUpdate; // the developer supplied a function to call when this binding updates, but // we don't really want to track any dependencies that would re-trigger this binding if (typeof afterUpdateHandler === "function") { ko.ignoreDependencies(afterUpdateHandler, viewModel, [value, color]); } $(element).somePlugin("value", value); } }
Computed observables are supposed to map a set of observable inputs into a single observable output. As such, it doesn’t make sense to include cycles in your dependency chains. Cycles would not be analogous to recursion; they would be analogous to having two spreadsheet cells that are computed as functions of each other. It would lead to an infinite evaluation loop.
So what does Knockout do if you have a cycle in your dependency graph? It avoids infinite loops by enforcing the following rule: Knockout will not restart evaluation of a computed while it is already evaluating. This is very unlikely to affect your code. It’s relevant in two situations: when two computed observables are dependent on each other (possible only if one or both use the deferEvaluation option), or when a computed observable writes to another observable on which it has a dependency (either directly or via a dependency chain). If you need to use one of these patterns and want to entirely avoid the circular dependency, you can use the peek function described above.
© Steven Sanderson, the Knockout.js team, and other contributors
Licensed under the MIT License.
http://knockoutjs.com/documentation/computed-dependency-tracking.html