Knock Me Out

Thoughts, ideas, and discussion about Knockout.js

Knockout in 2019

| Comments

Hello! Are you currently developing with Knockout? Much has changed in the landscape of client-side libraries since Knockout was considered a popular choice, but there still seem to be many successful projects using it today. I would love to hear about the interesting things that you are building with Knockout in 2019.

For the last several years, I have been working with React on the front-end (and Node on the server-side) while working on LeanKit. So, while I have been having lots of fun with JavaScript, I haven’t had a chance to do anything new with Knockout lately. I would really enjoy spending time getting caught up and helping out in the Knockout world.

Looking for feedback on these questions:

  • What would be useful for me to spend some time on?
  • What are your biggest challenges with Knockout in 2019?
  • Are there new plug-ins that could be created to solve a problem?
  • Should I update many of my existing libraries to have modern code and a up-to-date build system?
  • Any other ideas come to mind?

Reply in the comments or send me an email at ryan@knockmeout.net. Thanks!

Knockout.js 3.3 Released

| Comments

Knockout 3.3 is available now! This release features some nice refinements to the components functionality along with a number of other small enhancements and bug fixes. The full release notes can be found here.

In this release cycle, we welcomed Brian Hunt to the core team and he has brought some great energy and contributions to the project. Steve and Michael once again made the bulk of the major changes in this release along with a number of other pull requests from the community.

Components refinements

We received lots of great feedback regarding Knockout’s component functionality. Many developers seem to be having great success using this style of development. For 3.3, we focused on a few low-level enhancements that should provide some increased flexibility.

Synchronous Flag

Components were always rendered asynchronously previously, even when the template/viewModel were already cached. Now, the component registration can include a “synchronous” flag to indicate that the component should render synchronously, if it can. In the case where you have many nested components, this can help alleviate issues with reflows and/or flickering while rendering. The syntax would look like:

1
2
3
4
5
ko.components.register("my-component", {
    template: "<div data-bind=\"text: firstName\"></div>",
    viewModel: { require: "myModule" },
    synchronous: true
});

Working with component child nodes

In Knockout 3.3, you now have options for working with the child nodes contained inside of a component. There are three enhancements that go together targetting this area:

1- The template binding can accept an array of DOM nodes directly via a nodes option

2- If you are using a createViewModel function for your component, it will now receive the child nodes and you can determine how to expose them on your component’s view model for binding against the template binding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ko.components.register("accordion-item", {
    template: "<h2 data-bind=\"template: { nodes: headerNodes }\"></h2><div data-bind=\"template: { nodes: bodyNodes }\"></div>",
    viewModel: {
        createViewModel: function(params, componentInfo) {
            return {
                data: params.data,
                // grab the first node (as an array) for the header
                headerNodes: componentInfo.templateNodes.slice(0, 1),
                // grab any additional nodes for the body
                bodyNodes: componentInfo.templateNodes.slice(1)
            };
        }
    }
});

3- There is a new $componentTemplateNodes context variable that contains the nodes directly. In normal cases, this will allow you to avoid exposing DOM nodes in your view model and bind in the template directly.

For example, a component template might simply want to add some wrapping markup like:

1
<div class="my-component" data-bind="template: { nodes: $componentTemplateNodes, data: data }"></div>

You could then add the component to your page:

1
2
3
4
5
<my-component params="{ data: item }">
    <h1>My Data</h1>
    <p>Body one</p>
    <p>Body two</p>
</my-component>

If would still be possible to manipulate (slice) $componentTemplateNodes directly in the binding, depending on the complexity of the markup/scenario.

$component context variable

Sometimes inside of a component’s template, when looping through nested structures, you may want to bind up to the root of the component. Rather than trying to manage $parents[x], you can now use $component to get the nearest components root. This is a similar concept to $root for the entire application, but is specific to a component.

Other Enhancements

A few other enhancements that I think are interesting:

awake/sleep notifications from pure and deferred computeds

You can now subscribe to notifications from a pureComputed for when it wakes up and for when it goes to sleep (when nothing depends on it). Additionally, a deferred computed now notifies when it wakes up as well. The subscriptions would look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
this.fullName = ko.pureComputed(function() {
    return this.firstName() + " " + this.lastName();
}, this);

this.fullName.subscribe(function(value) {
    console.log("fullName is awake with a value: " + value);
}, this, "awake");

this.fullName.subscribe(function() {
    console.log("fullName is sleeping" + value);
}, this, "sleep");

this.fullPhoneNumber = ko.computed(function() {
    return this.areaCode() + " " this.phoneNumber();
}, this, { deferEvaluate: true });

this.fullPhoneNumber.subscribe(function(value) {
    console.log("fullPhoneNumber is awake with a value: " + value);
}, this, "sleep");

Exposing a few additional functions to the release build

  • ko.ignoreDependencies(callback, callbackTarget, callbackArgs) - executes a function and ignores any dependencies that may be encountered. This can be useful sometimes in a computed observable or in a custom binding when you want to execute code, but not trigger updates when any dependencies from that code change. You would use it like:
1
ko.ignoreDependencies(this.myAfterUpdateHandler, this, [true]);
  • ko.utils.setTextContent(element, textContent) handles cross-browser setting the text of a node and handles virtual elements

Fixes

3.3 also includes a number of nice fixes and performance improvements listed here. This includes making the css binding work properly with SVG elements, which has been a long-standing issue.

Please check out Knockout 3.3.0 today! It is available from GitHub, the main site, Bower (bower install knockout), and NPM (knockout).

Cleaning Up After Yourself in Knockout.js

| Comments

Last summer, I had the opportunity to speak at ModernWebConf, ThatConference, and devLink on the topic of browser memory leaks. The talk was focused on the tools and techniques that you can use for memory leak testing and situations in JavaScript that comonly cause these leaks. Slides for the presentation can be found here.

With the rise of single-page applications and increased complexity (and amount) of JavaScript on the client-side, memory leaks are a common occurrence. Knockout.js applications are not immune to these problems. In this post, I will review some scenarios that often contribute to memory leaks and discuss the APIs in Knockout that can be used to prevent and resolve these issues.

The main source of leaks in KO

Memory leaks in KO are typically caused by long-living objects that hold references to things that you expect to be cleaned up. Here are some examples of where this can occur and how to clean up the offending references:

1. Subscriptions to observables that live longer than the subscriber

Suppose that you have an object representing your overall application stored in a variable called myApp and the current view as myViewModel. If the view needs to react to the app’s language observable changing, then you might make a call like:

1
myApp.currentLanguage.subscribe(myCurrentView.languageHandler, myCurrentView);

What this technically does is goes to myApp.currentLanguage and adds to its list of callbacks with a bound function (languageHandler) that references myCurrentView. Whenever myApp.currentLanguage changes, it notifies everyone by executing each registered callback.

This means that if myApp lives for the lifetime of your application, it will keep myCurrentView around as well, even if you are no longer using it. The solution, in this case, is that we need to keep a reference to the subscription and call dispose on it. This will remove the reference from the observable to the subscriber.

1
2
3
4
myCurrentView.languageSubscription = myApp.currentLanguage.subscribe(myCurrentView.languageHandler, myCurrentView);

// somewhere later in the code, when you are disposing of myCurrentView
myCurrentView.languageSubscription.dispose();

2. Computeds that reference long-living observables

1
2
3
myCurrentView.userStatusText = ko.computed(function() {
    return myApp.currentUser() + " (" + myCurrentView.userStatus + ")";
}, myCurrentView)

In this case, the userStatusText computed references myApp.currentUser(). As in the subscription example, this will add to the list of callbacks that myApp.currentUser needs to call when it changes, as the userStatusText computed will need to be updated.

There are a couple of ways to solve this scenario:

  • we can use the dispose method of a computed, like we did with a manual subscription.
1
2
// in disposal code
myCurrentView.userStatusText.dispose();
  • in KO 3.2, a specialized computed called a ko.pureComputed was added (docs here). A pure computed can be created by using ko.pureComputed rather than ko.computed or by pasing the pure: true option when creating a normal computed. A pure computed will automatically go to sleep (release all of its subscriptions) when nobody cares about its value (nobody is subscribed to it). Calling dispose on a pure computed would likely not be necessary for normal cases, where only the UI bindings are interested in the value. This would work well for our scenario where a temporary view needs to reference a long-living observable.

3. Event handlers attached to long-living objects

In custom bindings, you may run into scenarios where you need to attach event handlers to something like the document or window. Perhaps the custom binding needs to react when the browser is resized. The target needs to keep track of its subscribers (like an observable), so this will create a reference from something long-living (document/window in this case) back to your object or element that is bound.

To solve this issue, inside of a custom binding, Knockout provides an API that lets you execute code when the element is removed by Knockout. Typically, this removal happens as part of templating or control-flow bindings (if, ifnot, with, foreach). The API is ko.utils.domNodeDisposal.addDisposeCallback and would be used like:

1
2
3
4
5
6
7
8
9
10
11
12
13
ko.bindingHandlers.myBinding = {
    init: function(element, valueAccessor, allBindings, data, context) {
        var handler = function () {
            // do something with element, valueAccessor, etc.
        };

        $(window).on("resize", handler);

        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            $(window).off("resize", handler);
        });
    })
};

If you did not have easy access to the actual handler attached, then you might consider using namespaced events like $(window).on("resize.myPlugin", handler) and then remove the handler with $(window).off("resize.myPlugin").

4. Custom bindings that wrap third-party code

The above issue is also commonly encountered when using custom bindings to wrap third-party plugins/widgets. The widget may not have been designed to work in an environment where it would need to be cleaned up (like a single-page app) or may require something like a destroy API to be called. When choosing to reference third-party code, it is worthwhile to ensure that the code provides an appropriate method to clean itself up.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ko.bindingHandlers.myWidget = {
    init: function (element, valueAccessor) {
        var myWidget = $(element).someWidget({
            value: valueAccessor()
        });

        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            // or whatever code is necessary to clean-up the widget
            if (myWidget && typeof myWidget.destroy === "function") {
                myWidget.destroy();
            }
        })
    }
};

Reviewing the tools/API for clean-up in Knockout

  1. dispose function. Can be called on a manual subscription or computed to remove any subscriptions to it.

  2. ko.utils.domNodeDisposal.addDisposeCallback - adds code to run when Knockout removes an element and is normally used in a custom binding.

  3. ko.pureComputed - this new type of computed added in KO 3.2, handles removing subscriptions itself when nobody is interested in its value.

  4. disposeWhenNodeIsRemoved option to a computed - in some cases, you may find it useful to create one or more computeds in the init function of a custom binding to have better control over how you handle changes to the various observables the binding references (vs. the update function firing for changes to all observables referenced. This technique can also allow you to more easily share data between the init function and code that runs when there are changes (which normally would be in the updatefunction.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ko.bindingHandlers.myBinding = {
    init: function (element, valueAccessor) {
        var options = valueAccessor();

        ko.computed(function () {
            var value = ko.unwrap(options.value);

            // do something with value
        }, null, { disposeWhenNodeIsRemoved: element });


        ko.computed(function () {
            var height = ko.unwrap(options.height);

            // do something with height
        }, null, { disposeWhenNodeIsRemoved: element });
    }
};

Note that the example is passing in the disposeWhenNodeIsRemoved option to indicate that these computeds should automatically be disposed when the element is removed. This is a convenient alternative to saving a reference to these computeds and setting up a handler to call dispose by using ko.utils.domNodeDisposal.addDisposeCallback.

Keeping track of things to dispose

One pattern that I have used in applications when I know that a particular module will often be created and torn down is to do these two things:

1- When my module is being disposed, loop through all top-level properties and call dispose on anything that can be disposed. Truly it would only be necessary to dispose items that have subscribed to long-living observables (that live outside of the object itself), but easy enough to dispose of anything at the top-level when some have created “external” subscriptions.

2- Create a disposables array of subscriptions to loop over when my module is being disposed, rather than assigning every subscription to a top-level property of the module.

A snippet of a module like this might look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var MyModule = function () {
    this.disposables = [];

    this.userStatus = ko.observable();

    this.userStatusText = ko.computed(this.getUserStatusText, this);

    this.disposables.push(myApp.currentLanguage.subscribe(this.handleLanguageChange, this));
};

ko.utils.extend(MyModule.prototype, {
    getUserStatusText: function() {
        return myApp.currentUser() + " (" + this.userStatus + ")";
    },

    handleLanguageChange: function(newLanguage) {
        // do something with newLanguage
    },

    dispose: function() {
        ko.utils.arrayForEach(this.disposables, this.disposeOne);
        ko.utils.objectForEach(this, this.disposeOne);
    },

    // little helper that handles being given a value or prop + value
    disposeOne: function(propOrValue, value) {
        var disposable = value || propOrValue;

        if (disposable && typeof disposable.dispose === "function") {
            disposable.dispose();
        }
    }
});

Conclusion

Memory leaks are not uncommon to find in long-running Knockout.js applications. Being mindful of how and when you subscribe to long-living observables from objects that are potentially short-lived can help alleviate these leaks. The APIs listed in this post will help ensure that references from subscriptions are properly removed and your applications are free of memory leaks.

Knockout.js 3.2 Preview : Components

| Comments

Knockout 3.2 will include some exciting new functionality out-of-the-box to do modular development through creating components. From Knockout’s point of view, a component allows you to asynchronously combine a template and data (a view model) for rendering on the page. Components in Knockout are heavily inspired by web components, but are designed to work with Knockout and all of the browsers that it supports (all the way back to IE6).

Components allow you to combine independent modules together to create an application. For example, a view could look like:

1
2
3
4
5
<myapp-nav></myapp-nav>

<myapp-grid params="data: items, paging: true, sorting: true"></myapp-grid>

<myapp-footer></myapp-footer>

The idea of doing modular development with Knockout is certainly not a new one. Libraries like Durandal with its compose binding and the module binding from my knockout-amd-helpers have been doing this same type of thing for a while and have helped prove that it is a successful way to build and organize Knockout functionality. Both of these libraries have focused on AMD (Asynchronous Module Definition) to provide the loading and organization of modules.

Knockout’s goal is to make this type of development possible as part of the core without being tied to any third-party library or framework. Developers will be able to componentize their code, by default, rather than only after pulling in various plugins. However, the functionality is flexible enough to support different or more advanced ideas/opinions through extensibility points. When KO 3.2 is released, developers should seriously consider factoring components heavily into their application architecture (unless already successfully using one of the other plugins mentioned).

How does it work?

By default, in version 3.2, Knockout will include:

  1. a system for registering/defining components
  2. custom elements as an easy and clean way to render/consume a component
  3. a component binding as an alternative to custom elements that supports dynamically binding against components
  4. extensibility points for modifying or augmenting this functionality to suit individual needs/opinions

Let’s take a look at how this functionality is used:

Registering a component

The default component loader for Knockout looks for components that were registered via a ko.components.register API. This registration expects a component name along with configuration that describes how to determine the viewModel and the template. Here is a simple example of registering a component:

1
2
3
4
5
6
ko.components.register("simple-name", {
    viewModel: function(data) {
        this.name = (data && data.name) || "none";
    },
    template: "<div data-bind=\"text: name\"></div>"
});

The viewModel key

  • can be a function. If so, then it is used as a constructor (called with new).
  • can pass an instance property to use an object directly.
  • can pass a createViewModel property to call a function that can act as a factory and return an object to use as the view model (has access to the DOM element as well for special cases).
  • can pass a require key to call the require function with the supplied value. This will work with whatever provides a global require function (like require.js). The result will again go through this resolution process.

Additionally, if the resulting object supplies a dispose function, then KO will call it whenever tearing down the component. Disposal could happen if that part of the DOM is being removed/re-rendered (by a parent template or control-flow binding) or if the component binding has its name changed dynamically.

The template key

  • can be a string of markup
  • can be an array of DOM nodes
  • can be an element property that supplies the id of an element to use as the template
  • can be an element property that supplies an element directly
  • can be a require property that like for viewModel will call require directly with the supplied value.

A component could choose to only specify a template, in cases where a view model is not necessary. The supplied params will be used as the data context in that case.

The component binding

With this functionality, Knockout will provide a component binding as an option for rendering a component on the page (with the other option being a custom element). The component binding syntax is fairly simple.

1
2
3
4
5
<div data-bind="component: 'my-component'"></div>

<div data-bind="component: { name: 'my-component', params: { name: 'ryan' } }"></div>

<!-- ko component: 'my-component' --><!-- /ko -->

The component binding supports binding against an observable and/or observables for the name and params options. This allows for handling dynamic scenarios like rendering different components to the main content area depending on the state of the application.

Custom Elements

While the component binding is an easy way to display a component and will be necessary when dynamically binding to components (dynamically changing the component name), custom elements will likely be the “normal” way for consuming a component.

1
<my-component params="name: userName, type: userType"></my-component>

Matching a custom element to a component

Knockout automatically does all of the necessary setup to make custom elements work (even in older browsers), when ko.registerComponent is called. By default, the element name will exactly match the component name. For more flexibility though, Knockout provides an extensibility point (ko.components.getComponentNameForNode) that is given a node and expected to return the name of the component to use for it.

How params are passed to the component

The params are provided to initialize the component, like in the component binding, but with a couple of differences:

  • If a parameter creates dependencies itself (accesses the value of an observable or computed), then the component will receive a computed that returns the value. This helps to ensure that the entire component does not need to be rebuilt on parameter changes. The component itself can control how it accesses and handles any dependencies. For example, in this case:
1
<my-component params="name: first() + ' ' + last()"></my-component>

The component will receive a params object that contains a name property that is supplied as a computed in this case. The component can then determine how to best react to the name changing rather than simply receiving the result of the expression and forcing the entire component to re-load on changes to either of the observables.

  • The params object supplied when using the custom element syntax will also include a $raw property (unless the params happens to supply a property with that same name) which gives access to computeds that return the original value (rather than the unwrapped value). For example:
1
<my-component params="value: selectedItem().value"></my-component>

In this case, since selectedItem is accessed, the param is supplied as a computed. When the computed is accessed, the unwrapped value is returned to avoid having to double-unwrap a param to get its value. However, you may want access to the value observable in this case, rather than its unwrapped value. In the component, this could be achieved by accessing params.$raw.value(). The default functionality is slanted towards ease of use (not having to unwrap a param twice) while providing $raw for advanced cases.

Custom loaders

Knockout let’s you add multiple “component loaders” that can choose how to understand what a component is and how to load/generate the DOM elements and data.

A loader provides two functions: getConfig and loadComponent. Both receive a callback argument that is called when the function is ready to proceed (to support asynchronous operations).

  • getConfig can asynchronously return a configuration object to describe the component given a component name.
  • loadComponent will take the configuration and resolve it to an array of DOM nodes to use as the template and a createViewModel function that will directly return the view model instance.

The default loader

To understand creating a custom component loader, it is useful to first understand the functionality provided by the default loader:

The default getConfig function does the following:

  • this function simply looks up the component name from the registered components and calls the callback with the defined config (or null, if it is not defined).

The default loadComponent function does the following:

  • tries to resolve both the viewModel and template portions of the config based on the various ways that it can be configured.
  • if using require will call require with the configured module name and will take the result and go through the resolution process again.
  • when it has resolved the viewModel and template it will return an array of DOM nodes to use as the template and a createViewModel function that will return a view model based on however the viewModel property was configured.

A sample custom loader

Let’s say that we want to create a widget directory where we place templates and view model definitions that we want to require via AMD. Ideally, we want to just be able to do:

1
<div data-bind="component: 'widget-one'"></div>

In this case, we could create a pretty simple loader to handle this functionality:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//add a loader to the end of the list of loaders (one by default)
ko.components.loaders.push({
    getConfig: function(name, callback) {
        var widgetName;

        //see if this is a widget
        if (name.indexOf("widget-") > -1) {
            widgetName = name.substr(7).toLowerCase();

            //provide configuration for how to load the template/widget
            callback({
                viewModel: {
                    //require the widget from the widget directory with just the name
                    require: "widgets/" + widgetName
                },
                template: {
                    //use the text plugin to load the template from the same directory
                    require: "text!widgets/" + widgetName + ".tmpl.html"
                }
            });
        } else {
            //tell KO that we don't know and it can move on to additional loaders
            callback(null);
        }
    },
    //use the default loaders functionality for loading
    loadComponent: ko.components.defaultLoader.loadComponent
});

In this custom loader, we just dynamically build the configuration that we want, so we don’t necessarily have to register every “widget” as its own component, although registering will properly setup custom elements to work with the component. Loading the widget-one component would load a one.js view model and one.tmpl.html template from a widgets directory in this sample loader. If the component is not a “widget”, then the callback is called with null, so other loaders can try to fulfill the request.

Summary

Components are a major addition to Knockout’s functionality. Many developers have found ways to do this type of development in their applications using plugins, but it will be great to have standard support in the core and the possibility for extensibility on top of it. Steve Sanderson recently did a great presentation at NDC Oslo 2014 that highlighted the use of components in Knockout. Check it out here.

Knockout 3.2 is well underway and should be ready for release this summer.

How Does Dependency Detection Work in Knockout.js?

| Comments

I received a question over email asking about how Knockout’s dependency detection actually works and thought that I would share an answer in this post. I know that I feel a bit uncomfortable whenever a library that I am using does something that I don’t fully understand, so I hope that I can help ensure that this part of Knockout is not misunderstood or considered “magic”.

A few questions to answer:

  1. For a computed observable, how does KO know which dependencies should trigger a re-evaluation of the computed on changes?
  2. How is it possible for the dependencies to change each time that a computed is evaluated?
  3. For bindings, how are dependencies tracked?

Determining dependencies for a computed

TLDR: Knockout has a middle-man object that is signalled on all reads to computed/observables and tells the current computed being evaluated that it might want to hook up a subscription to this dependency.

  • Internally Knockout maintains a single object (ko.dependencyDetection) that acts as the mediator between parties interested in subscribing to dependencies and dependencies that are being accessed. Let’s call this object the dependency tracker.

  • In a block of code that wants to track dependencies (like in a computed’s evaluation), a call is made to the dependency tracker to signal that someone is currently interested in dependencies. As an example, let’s simulate what a computed would call:

1
2
3
4
5
6
7
8
9
10
11
//a few observables to work with
var test = ko.observable("one"),
    test2 = ko.observable("two"),
    test3 = ko.observable("three");

//a computed internally ask to start tracking dependencies and receive a notification when anything observable is accessed
ko.dependencyDetection.begin({
    callback: function(subscribable, internalId) {
        console.log("original context: " + internalId + " was accessed");
    }
});
  • Any read to an observable or computed triggers a call to the dependency tracker. A unique id is then assigned to the observable/computed (if it doesn’t have one) and the callback from the currently interested party is passed the dependency and its id.
1
2
3
4
5
//access an observable
test();

//output:
//original context: 1 was accessed
  • The dependency tracker maintains a stack of interested parties. Whenever a call is made to start tracking, a new context is pushed onto the stack, which allows for computeds to be created inside of computeds. Any reads to dependencies will go to the current computed being evaluated.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//start another dependency detection inside of the current one
ko.dependencyDetection.begin({
    callback: function(subscribable, internalId) {
        console.log("    child context: " + internalId + " was accessed");
    }
});

//inside of child context again access test and also test2
test();
test2();

//output
//child context: 1 was accessed
//child context: 2 was accessed
  • When a computed is done evaluating, it signals the dependency tracker that is is complete and the tracker pops the context off of its stack and restores the previous context.
1
2
3
4
5
6
7
8
9
10
11
//end child context
ko.dependencyDetection.end();

//dependencies are back to the original (outer) context
test3();

//output
//original context: 3 was accessed

//end original context
ko.dependencyDetection.end();
  • When an observable/computed dependency is updated, then the subscription is triggered and the computed is re-evaluated.

Here is a jsFiddle version of this code: http://jsfiddle.net/rniemeyer/F9CrA/. Note that ko.dependencyDetection is only exposed in the debug build. In the release build it is renamed as part of the minification process.

So, Knockout doesn’t need to parse the function as a string to determine dependencies or do any “tricks” to make this happen. The key is that all reads to observable/computeds go through logic that is able to signal the dependency tracker who can let the computed know to subscribe to the observable/computed.

How can dependencies change when a computed is re-evaluated?

Each time that a computed is evaluated, Knockout determines the dependencies again. For any new dependencies, a subscription is added. For any dependencies that are no longer necessary, the subscriptions are disposed. Generally, this is efficient and beneficial as long as you are only branching in computed code based either data that is observable or doesn’t change. For example:

1
2
3
4
5
6
7
8
9
10
11
 this.errors = ko.computed(function() {
     var result = [];

     if (this.showErrors()) {
         result = ko.utils.arrayFilter(this.history(), function(item) {
            return item.type() === "error";
         });
     }

     return result;
 }, this);

In this example, when showErrors is false, this computed will only have a single dependency, showErrors. There is no need to depend on the history or trigger re-evaluation when the items change, as it will not influence the result of the function. If showErrors does become truthy, then the computed will depend on showErrors, the history observableArray, and the type of each item.

What about bindings? How do they track dependencies?

Bindings in Knockout actually use computeds as a tool to facilitate their own dependency tracking. Each binding is evaluated within a computed observable for this purpose. Observables/computeds accessed in the update function of a binding become dependencies. Observables that are accessed within a binding string (like items in data-bind="if: items().length") are read when the valueAccessor() function is called (or via allBindingsAccessor - allBindingsAccessor.get("if") in this case). I think that it is useful to think of a binding’s update function just like a normal computed observable where access to any dependencies will trigger the binding to run again.

Note: Prior to KO 3.0, all bindings on a single element were wrapped inside of a single computed. The parsing and evaluation of the binding string was also included in this computed. So, calling valueAccessor() would give the result of the expression rather than actually run the code. Dependency detection worked the same way, all bindings on an element were triggered together and it was not possible to isolate dependencies made in the binding string. See this post for more details.

Something new in KO 3.2 - ko.pureComputed

There is an interesting feature coming in KO 3.2 related to computed dependency tracking. Michael Best implemented an option that allows a computed to not maintain any dependencies when it has no subscribers to it. This is not appropriate for all cases, but in situations where the computed returns a calculated value with no side-effects, if there is nothing depending on that calculated value, then the computed can go to “sleep” and not maintain subscriptions or be re-evaluated when any of the dependencies change. This will be useful for efficiency as well as potentially preventing memory leaks for a computed that was not disposed, but could be garbage collected if it was not subscribed to something observable that still exists. The option is called pure and a ko.pureComputed is provided as a shortcut.