Steve Michelotti

A .NET Developer's Toolbox

  Home  |   Contact  |   Syndication    |   Login
  199 Posts | 0 Stories | 1106 Comments | 51 Trackbacks

News

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers




Google My Blog

What I'm Reading:

Shelfari: Book reviews on your book blog

Tag Cloud


Archives

Post Categories

Code

Publications

Tuesday, October 11, 2011 #

KnockoutJS is an awesome MVVM JavaScript data binding framework for building rich user experiences. As sites get more interactive and more presentation logic gets pushed to the client-side, it’s important to have rich frameworks which enable these scenarios. The next version of MVC is going to include the Knockout NuGet package in the box. Now is a perfect time to start learning Knockout.

As I’ve been learning Knockout, I’ve been pleased to discover that, although it comes with a rich set of features built-in, one of the best aspects of Knockout is the extensibility. When there is a behavior you need that doesn’t match the out of the box capabilities of Knockout it’s easy to customize. Typically this takes the form of building your own custom binding. Let’s take an example.

A typical Knockout scenario is when you have a text box which is used to add an item to a list. From the jsFiddle below you the see the JavaScript and HTML for this are very straight-forward. If you click the “Result” tab, you will see that any item you type in the text box will be added to the list when you click the “Add” button:

 

Direct link to the jsFiddle.

 

This is all well and good but it would be more convenient for the user if they did not have to pick up their mouse to click the Add button for each item – but instead were able to just hit the Enter key to add the item. This can be done with relative ease by using the event binding (in conjunction with the keypress event) that comes with Knockout as shown in the next jsFiddle:

 

Direct link to the jsFiddle.

 

As you can see in the JavaScript above, the addOnEnter function checks the charCode of 13 (i.e., the Enter key) and invokes the addItem() function for this scenario.

We now have the behavior we want. However, the problem here is that my addOnEnter function() is not a re-usable solution at all because the call to the specific addItem function is directly embedded inside. If we have another text box that should add to another collection, we can’t use the above solution without some copy/paste ugliness replacing the 1-line of code to call the appropriate “add” function. Fortunately, the re-usability problem can be solved easily be created your own custom binding. Basically what we’re after is the ability to have our own binding called “executeOnEnter” which automatically checks the Enter key press and provides the ability to specify the function that should be called. In other words, we’d like to declaratively be able to specify this:

   1:  <input type="text" data-bind="value: itemToAdd, valueUpdate: 'afterkeydown', executeOnEnter: addItem" />

In order to do this, we have to write our own custom binding for executeOnEnter which is shown in the following jsFiddle:

 

Direct link to the jsFiddle.

 

The key thing to notice is that we are passing in the reference to the function that we want to be executed. This happens automatically via the “allBindingsAccessor” variable that is available for all custom bindings. However, instead of invoking the function directly, I use the JavaScript call() function and pass in the viewModel (conveniently, the viewModel is another parameter that is automatically available for all custom bindings). In JavaScript, the call() method assigns the this pointer for the specific method invocation. If we didn’t do this, this addItem() method wouldn’t work properly because the this pointer would be in the context of the custom binding rather than the context of the view model. It would force us to have to write our addItem() method like this:

   1:  addItem: function() {
   2:      viewModel.items.push({ name: viewModel.itemToAdd() });
   3:      viewModel.itemToAdd("");
   4:  } 

rather than like this (much preferred):

   1:  addItem: function() {
   2:      this.items.push({ name: this.itemToAdd() });
   3:      this.itemToAdd("");
   4:  } 

 

This is just the tip of the iceberg for the types of things you can do with custom bindings with Knockout!