Knock Me Out

Thoughts, ideas, and discussion about Knockout.js

Working With AMD Modules in Knockout.js

| Comments

After being a bit resistant at first, I have happily moved to using require.js to manage dependencies in most of the Knockout.js applications that I have written in the last year or so. With each application, I have tried a number of patterns to make it easier to work with AMD (Asynchronous Module Definition) modules. Recently, I decided to formalize some of these patterns into a lightweight plugin that makes it simple to bind against modules and pull in templates that live in external files.

The result is this library: knockout-amd-helpers

Why require.js and AMD modules

Breaking an application down into small, decoupled modules is a pattern that works well when done right. Creating modules in JavaScript is often accomplished by using various namespacing patterns. As an application grows though, it is easy to lose sight of the individual dependencies of each module and eventually maintaining the order that all of your scripts need to be loaded in becomes a burden as well. AMD module loaders can help with these issues.

There are plenty of good resources that help explain require.js, dependency management, and AMD modules. Here are a few good ones:

  • The require.js documentation on AMD modules.
  • Our documentation on using Knockout with require.js.
  • My co-worker Jonathan Creamer’s tutorial on using Knockout.js in large-scale applications. He also recently did a presentation for dotnetConf that can be found here on using require.js and using it with ASP.NET MVC.
  • While require.js is the most mentioned option in this category, there are others including the excellent curl.js.

A few pain points

When developing Knockout applications using require.js, there are a couple of situations that out-of-the-box still seemed less than perfect to me.

1- After breaking down your application into specific modules, it is unfortunate to have to include all of your markup on a single page. There are server-side solutions to pull in partials for your templates, but that still results in a page that contains all of your markup from the start. There is also the external template engine, which works well, but is not designed to leverage the capabilities of the AMD loader libraries (like optimization/bundling).

2- Dynamically requesting and binding against modules is not trivial. Normally, you would build a main “App” view model that contains all of the sub-modules that you want to bind against. It would be nice though to be able to dynamically pull in modules or use modules as reusable components in an app under any context.

Looking at potential solutions

Based on some of the different approaches that I have used in the past, I created the knockout-amd-helpers plugin to hopefully help ease these issues.

An AMD compatible template engine

The first feature of the plugin is to use one of Knockout’s extensibility points to replace the default template engine with one that is able to load named templates using the AMD loader’s text plugin.

I used the template sources extensibility point that I discussed here. When a named template is requested, it first checks to see if there is a script tag with the specified id (the default engine actually will grab any element with that id) and if not uses the text plugin to require the template. This will asynchronously load the template (unless it has already been loaded or is bundled and already available on the client). The template source uses an observable that triggers the template binding to update when the template is available.

For example, when using a binding like:

1
    <ul data-bind="template: { name: 'itemView', foreach: items }"></ul>

If there is no script tag with an id of itemView, it will attempt to load the template. The engine uses a default path of templates and a default suffix of .tmpl.html. So, it would look for the template at templates/itemView.tmpl.html. The default path and suffix can be configured and you can also pass a more specific path in for the name (sub/path/itemView).

A module binding

This updated template engine now helps keep your templates as modular as your view models, but it still would be nice to easily pull modules into an application without using a top-level view model that needs to contain everything that you might want to bind against.

To help with this situation, the plugin includes a module binding, which offers a flexible way to dynamically pull a module into your markup. Here is a basic example:

1
    <nav data-bind="module: 'navigation'"></div>

This will require a navigation module (the base directory can be configured as well). If the main element had children, then it would use them as an inline/anonymous template. However, in this case, since the element does not have children, it will use navigation as the template name and follow the template engine’s rules for pulling in the template.

After the loading the module it will follow a few rules for deciding what data to bind against:

  1. If the module returns a function, then it will create a new instance
  2. If the module returns an object, then it will, by default, call an initialize function on the object (if it exists) and either use the result of the function or the original object if there is not a return value.

This gives you the flexibility of either constructing a new object, using an existing object, or calling a function that returns some object to bind against.

The module binding has a number of options that you can pass in as well. Here is an example passing all of the options:

1
2
    <div data-bind="module: { name: 'one', data: initialData, template: 'oneTmpl',
                              initializer: 'createItem', afterRender: myCallback }"></div>

These options let you do things like customize the template to use, specify data to pass into the constructor or initializer, define the name of the function to call, and pass an afterRender function through to the template binding.

With this binding, it is possible to call ko.applyBindings({}) and build your application strictly using the module binding. A module binding can be nested inside other module bindings and you can also use observables to dynamically specify your module.

Communicating between modules

When building an app using this structure, it would be less than ideal to rely on $root or $parent calls within the markup to communicate between modules. This can potentially couple your module to only working within a certain context. While this can certanily work, a better solution may be to use some type of messaging to communicate between the modules.

I have a library called knockout-postbox that adds a ko.postbox object and some observable extensions that make it easy to publish values on a topic and update an observable based on a subscription to a topic.

For example, in a module that defines the main content, you could have an observable like:

1
   this.sectionName = ko.observable().subscribeTo("navigation.current");

Then, in the navigation module, publish on that topic like:

1
   this.selectedNavItem = ko.observable().publishOn("navigation.current");

The postbox library supports a number of options that control how the publish/subscribe process works, but if you are looking for a stand-alone library that supports a number of additional features (channels, wildcards, envelopes), then I would recommend postal.js

Summary

If you are using AMD modules in your Knockout application and are looking for a lightweight and simple, but flexible way to bind against templates and modules, then check out this new library. It has been tested with both require.js and curl.js. I would be happy to help support any other AMD loaders, if there is interest. Please check out the README on the repository for additional documentation. I also plan to work on a better example, as time permits.

Joining appendTo

| Comments

Just a quick note that I joined appendTo as a senior JavaScript engineer at the beginning of March. I am really excited to work with a great group of talented people and to get a chance to work on a wide variety of web development projects. I am especially eager to use some new technologies/libraries to solve real problems and can’t wait to gain new perspectives on ways to improve or build on top of Knockout.

If you are looking for help with a significant Knockout project (or any other front-end technology), then I would encourage you to contact appendTo and maybe we could have the chance to work together. For general KO questions or if you are looking for help on a project that only requires a small time investment, feel free to contact me directly.

Tekpub Refactoring Knockout.js Screencast

| Comments

Recently, I had the chance to do some pair programming with Rob Conery to refactor a Knockout-based shopping cart for a Tekpub full throttle video. Rob has had a love/hate relationship with Knockout over the years and I have had several discussions with him in the past trying to work through some of his concerns.

He recently asked me to take a look at a Knockout-based shopping cart that he had written for another video. The code was working just fine, but as I dug into it, I started jotting down a LOT of notes. I ended up formatting them in markdown and sent them Rob’s way. He thought that it would be a good idea to record a screencast to really dig into those notes.

Here is a trailer for the screencast:

I personally have had a Tekpub subscription for around two years and definitely recommend the service. They are really putting out some great content lately and have a well-done Knockout series.

Update: here is a link to the notes that I sent Rob.

A Simple Editor Pattern for Knockout.js

| Comments

Implementing an editor that allows users to accept or cancel their changes is a common task in Knockout.js. Previously, I suggested the idea of a protectedObservable that is an extended observable with the ability to commit and reset the value being edited. Since Knockout 2.0, I would probably now implement that functionality using either extenders or by augmenting the .fn object of the core types. However, I now use a different pattern.

I described this pattern in the Twin Cities Code Camp presentation here. This technique allows you to easily copy, commit, and revert changes to an entire model. There are two rules that make this pattern work:

  1. The creation of observables and computeds needs to be separate from actually populating them with values.

  2. The format of the data that you put in should be the same as the format that you are able to get out. Typically this means that calling ko.toJS on your model should result in an object that you could send back through the function described in step 1 to re-populate the values.

Separate creation and initialization

The idea is that we can apply fresh data to our object at anytime, so our constructor function should just create our structure, while a separate method handles setting the values.

1
2
3
4
5
6
7
8
9
10
11
    var Item = function(data) {
        this.name = ko.observable();
        this.price = ko.observable();

        this.update(data);
    };

    Item.prototype.update = function(data) {
        this.name(data.name || "new item");
        this.price(data.price || 0);
    };

Our update function (call it whatever you like init, initialize, hydrate, etc.), needs to handle populating the values of all of the observables given a plain JavaScript object. It can also handle supplying default values.

Data in matches data out

To make this pattern work, we need to be able to put a plain JavaScript object in and get the equivalent object out. Ideally, simply calling ko.toJS on our model, will give us the plain object that we need. It is okay, if there are some additional computeds/properties on the object, but it needs to be appropriate for sending through the update function again.

A technique that I use frequently to “hide” data that I won’t want when I turn the model into a plain JavaScript object or to JSON is to use a “sub-observable”. So, if we had a computed observable to show a formatted version of the price that we don’t want in our output, we could specify it like:

1
2
3
4
5
6
7
    var Item = function(data) {
        this.name = ko.observable();
        this.price = ko.observable();
        this.price.formatted = ko.computed(this.getFormattedPrice, this);

        this.update(data);
    };

Since observables are functions and functions are objects that can have their own properties, it is perfectly valid to create a formatted computed off of the price observable. In the markup, we can bind against price.formatted. However, when calling ko.toJS or ko.toJSON, the formatted sub-observable will disappear, as we will simply be left with a price property and value.

Reverting changes

Now that we have these pieces in place, it is easy to create an editor that allows reverting changes. In the event that a user chooses to cancel their editing, all we need to do is send the original data back through our update function and we will be back to where we started.

We will need to track the original data, so that it is available to repopulate the model. A good place to cache this data is in the update function itself. It is useful to make sure that the data is “hidden” as well, so multiple edits don’t result in recursive versions of the old data being kept around. If we were not using the prototype, then we could just use a local variable to store the data, but since we are placing our shared functions on the prototype in these samples, we need to find another way to hide this data. A simple solution is to create an empty cache function and store our data as property off of it. This will prevent calls to ko.toJS or ko.toJSON from capturing this data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    var Item = function(data) {
        this.name = ko.observable();
        this.price = ko.observable();
        this.cache = function() {};

        this.update(data);
    };

    ko.utils.extend(Item.prototype, {
      update: function(data) {
        this.name(data.name || "new item");
        this.price(data.price || 0);

        //save off the latest data for later use
        this.cache.latestData = data;
      },
      revert: function() {
        this.update(this.cache.latestData);
      }
    });

In this scenario, we will let edits persist to our observables and in the event that a user chooses to cancel, we will refresh our model with the original data.

Committing changes

When a user accepts the data, we need to make sure that we update the cached data with the current state of the model. For example, we could simply add a commit or accept function to our prototype that does:

1
2
3
    commit: function() {
        this.cache.latestData = ko.toJS(this);
    }

We now have the latest data cached, so the next time that a user cancels it will be reverted back to this updated state.

Copying/cloning

Sometimes in an editor, we would not want the currently edited observables to affect the rest of the UI until the user chooses to accept the changes. In this case, we can use these same methods to create a copy of the item for editing and then apply it back to the original. Here is how our overall view model might look:

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
34
35
36
37
38
    var ViewModel = function(items) {
        //turn the raw items into Item objects
        this.items = ko.observableArray(ko.utils.arrayMap(items, function(data) {
            return new Item(data);
        });

        //hold the real currently selected item
        this.selectedItem = ko.observable();

        //make edits to a copy
        this.itemForEditing = ko.observable();
    };

    ko.utils.extend(ViewModel.prototype, {
        //select and item and make a copy of it for editing
        selectItem: function(item) {
            this.selectedItem(item);
            this.itemForEditing(new Item(ko.toJS(item)));
        },

        acceptItem: function(item) {
            var selected = this.selectedItem(),
                edited = ko.toJS(this.itemForEditing()); //clean copy of edited

            //apply updates from the edited item to the selected item
            selected.update(edited);

            //clear selected item
            this.selectedItem(null);
            this.itemForEditing(null);
        },

        //just throw away the edited item and clear the selected observables
        revertItem: function() {
            this.selectedItem(null);
            this.itemForEditing(null);
        }
    });

So, we make edits to a copy of the original and on an accept we apply those edits back to the original. We can use ko.toJS to get a plain JavaScript object and feed it into our update function to apply the changes. In this scenario, the revertItem function can simply throw away the copied item and clear our the selected value.

Link to sample on jsFiddle.net

If you were going to reuse this technique often, then you could even create an extension to an observableArray that adds the appropriate observables and functions off of the observableArray itself like in this sample.

Updating from server

Our update function is also a handy way to apply updates from the server to our existing view model. In our case, we would likely need to add an id to the Item objects, so that we can identify which object needs updating. This works very well with something like SignalR or socket.io to keep various clients in sync with each other. This is an easier pattern than trying to swap items in an array or update specific properties one by one.

Summary

Editors with accept/cancel options are a common scenario in a Knockout.js application. By separating the creation of observables/computeds from poplating their values, we can commit, reset, and update models by feeding a version of the data through our update function. When you think in terms of the plain JS object that you can get out of or put into a model, it makes many of these scenarios easy to handle.

Revisting Event Delegation in Knockout.js

| Comments

Previously, I discussed a technique to do event delegation in Knockout.js. That post was created before Knockout 2.0, which added the ability to use ko.dataFor and ko.contextFor to retrieve the data context related to a specific element (as described here). With these new tools, there are now much better ways to add a single handler on a parent element that can respond to events triggered on its child elements.

Note: based on the techniques in this post, I created a plugin called knockout-delegatedEvents located here: https://github.com/rniemeyer/knockout-delegatedEvents.

Why event delegation in Knockout.js?

Currently when you use the click or event binding in Knockout, an event handler is attached directly to that element. Generally this is fine and causes no real problems. However, if you face a scenario where you have a large number of elements that require these bindings, then there can be a performance impact to creating and attaching these handlers to each element, especially in older browsers. For example, you may be creating a grid where each cell needs various event handlers or a hierarchical editor where you are potentially attaching handlers to interact with the parent items as well as each child (and each child may have children).

In this case, there are advantages to using event delegation:

  • you only have to attach one (or a few) event handlers rather than one on each element. Events bubble up to the higher-level handler and can understand the original element that triggered it.
  • dynamically added content does not need new event handlers added to them (largely not an issue with KO bindings and templating)

Normal event delegation with KO 2.0+

With Knockout 2.0+, the common way to use event delegation is to avoid attaching event handlers in the bindings themselves and add another layer of code to attach a single handler to a parent element using something like jQuery’s on (previously live/delegate). So, rather than doing:

1
2
3
4
5
<ul data-bind="foreach: items">
    <li>
        <a href="#" data-bind="click: $root.selectItem, text: name"></a>
    </li>
</ul>

You would instead do:

1
2
3
4
5
<ul id="items" data-bind="foreach: items">
    <li>
        <a href="#" data-bind="text: name"></a>
    </li>
</ul>

and attach an event handler elsewhere like:

1
2
3
4
5
6
$("#items").on("click", "a", function() {
    var context = ko.contextFor(this); //this is the element that was clicked
    if (context) {
        context.$root.selectItem(context.$data);
    }
});

Attaching a single handler like this works quite well. However, there are a few issues that I still have with this technique:

  • you have another layer of code to manage besides your view model. This is the type of code that Knockout generally helps you get away from.
  • this code is tightly coupled (fairly) with this view’s markup
  • in our example, if the entire ul is part of a template that gets swapped, then we would need to hook up our handler again or choose to add our handler at a higher level (possibly up to the body)
  • this particular code also adds a dependency on jQuery that is not really necessary

In my perfect Knockout application, you have your view and your view model with the only code outside of that being a call to ko.applyBindings. To accomplish this, we would need to be able to wire it up declaratively in the view. I have been exploring a few ways to make this happen.

Declaratively adding a handler

The first step is attaching a handler on the parent or root element that will respond when events bubble up to it. This is pretty easy to accomplish with a custom binding. When the event is handled, we can determine the original element and use ko.dataFor or ko.contextFor to get the original data context and then execute some method.

1
2
3
4
5
6
7
8
9
10
//add a handler on a parent element that reponds to events from the children
ko.bindingHandlers.delegatedHandler = {
    init: function(element, valueAccessor) {
        //array of events
        var events = ko.utils.unwrapObservable(valueAccessor()) || [];
        ko.utils.arrayForEach(events, function(event) {
            ko.utils.registerEventHandler(element, event, createDelegatedHandler(event, element));
        });
    }
};

To create the handler, we take the target element and get its context:

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
//create a handler for a specific event like "click"
var createDelegatedHandler = function(eventName, root) {
    //return a handler
    return function(event) {
        var el = event.target,
            data = ko.dataFor(el),
            action = findAnAction(el, context); //??? how do we know what method to call


        //execute the action
        if (action) {
            //call it like KO normally would with the data being both the value of `this` and the first arg
            result = action.call(data, data, event);

            //prevent the default action, unless the function returns true
            if (result !== true) {
                if (event.preventDefault) {
                    event.preventDefault();
                }
                else {
                    event.returnValue = false;
                }
            }
        }
    };
};

When used on an element, the binding would take in an array of events like:

1
2
3
<ul data-bind="delegatedHandler: ['click', 'mouseover', 'mousedown']">
...
</ul>

With this binding in place, we can handle the event, find the element that triggered the event, and determine the data context of the element. However, we still need to know what method to call. I looked at a few alternatives for this piece.

Option #1 - using a binding

On the child elements, we could use a binding that stores a reference to the function to call. The binding can use Knockout’s ko.utils.domData functions to store and retrieve data on the element, as Knockout automatically cleans up this data whenever it removes elements from the document. For example, we could create a delegatedClick binding to make this association.

1
2
3
4
5
6
7
//associate a handler with an element that will be executed by the delegatedHandler
ko.bindingHandlers.delegatedClick = {
    init: function(element, valueAccessor) {
        var action = valueAccessor();
        ko.utils.domData.set(element, "ko_delegated_click", action);
    }
};

Now, on a child element, you would associate a function like:

1
2
3
<li>
    <a href="#" data-bind="text: name, delegatedClick: $parent.selectItem"></a>
</li>

We could create additional bindings for other events that we are interested in and could even create a helper function to generate these bindings given an array of events that we need to handle.

Pros

  • Can simply replace click bindings with delegatedClick (and other events) without any other changes
  • Can bind against specific functions and can execute code for cases where you need to call a function with a parameter to create a handler (delegatedClick: $parent.createHandler('some_modifier'))

Cons

  • The binding on the child elements is lightweight, but we still have to pay the overhead of parsing and executing a binding.

Option #2 - use a data- attribute with method name

The first solution works well, but I still do not like having to pay the price of executing bindings on the child elements. For an alternative solution, I looked at a different approach where the child elements are tagged with a data-eventName attribute that contains the method name.

1
2
3
<li>
    <a href="#" data-click="selectItem" data-bind="text: name"></a>
</li>

With this technique we would no longer need the child bindings. In the event handling code, we would now need to locate a method that matches our data-eventName attribute value. This time we can use ko.contextFor to get back the entire binding context. It is very common to call functions off of $parent or $root as well as off of the current data itself, so the $parents array allows us to walk up the scope chain to locate an appropriate method.

Pros

  • No overhead of executing a binding on the child elements
  • Can execute the function with the correct owner, so we do not need to use .bind or var self = this; to ensure that the context is correct. This is a really nice benefit, we have to search for the method in each scope, so we know the appropriate owner of the method to use as the context.

Cons

  • could run into issues with clashing names (child has an add method, but you wanted to call parent’s add)
  • cannot execute code to create a handler, as the attribute value would be a string that needs to match a method name

Option #3 - associate method and name in root/global object

Another alternative might be to use the data- attribute, but use the string to key into an object that holds the associations between names and actions. Perhaps something like ko.actions or ko.commands. This object could hold just function references or maybe a function and owner. For arrays of objects, we would not be able to indicate an appropriate owner, so we would have to fall back to making sure that the function is properly bound on each instance. For example, we could even give the function an alias of select like:

1
2
3
4
ko.actions.select = {
    action: this.selectItem,
    owner: this
};

Pros

  • No overhead of executing a binding on the child elements
  • No issues with ambiguity in names, as names can be aliased
  • Functions outside of a view model could be included
  • Could dynamically change the definition of the action

Cons

  • have to manage adding (and maybe removing) functions to this object
  • different types of objects with the same method names would have to be aliased with unique names in this object

Summary

In cases where you need to attach a large number of event handlers, using event delegation can be a nice win. Attaching these handlers declaratively provides an alternative to wiring this up outside of your view/viewmodel. While considering each of the techniques to determine the function to execute, I was leaning towards #2, as it does not require the child bindings and as a bonus gets the context right when it calls the handler. Instead though, I decided to create a plugin that can use all of these techniques interchangeably. The plugin lives at: http://github.com/rniemeyer/knockout-delegatedEvents. Please let me know if you have feedback or suggestions for the plugin.