Knock Me Out

Thoughts, ideas, and discussion about Knockout.js

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.

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.