Knockout.js – Bindings

Last time I introduced the basics of the knockout.js library. We looked at how you can declare a very simple model and then bind it to a UI using ‘data-bind’ attributes. I alluded to the fact that there are more bindings that come with knockout.js.

Simply put, a binding is something that ties some property (or method) to the HTML DOM. Some bindings are one-way and some are (or at least can be) two-way. In the last post we used the ‘text’ and ‘click’ bindings. ‘text’ can be a two-way binding. I say ‘can be’ because it can only be a two way binding if you initialize the bound model property using ko.observable(). If the bound property is initialized as a primitive javascript type (ie. string or number, etc), the binding will be one-way.

The real power of bindings comes into play when you start binding more than just text fields. Using bindings you can have the methods (behavior) on your javascript objects bound to different events on the DOM elements, such as ‘click’ or ‘event’. Using knockout bindings you can remove alot of that jQuery code to hook events and simply mark up your HTML with a few data-bind=”” attributes.

Let’s look at a small example. We’ll start with the model again:

var model = {
    firstName: ko.observable(""),
    lastName: ko.observable(""),
    submit: function() {
        var shouldSubmit = confirm("Are you really sure?");
        return shouldSubmit;
    }
}

model.isValid = ko.dependentObservable(function() {
    return this.firstName().length > 0 && this.lastName().length > 0;
}, model);

ko.applyBindings(model);

As you can see we have a couple of simple properties (firstName, lastName), but then we have two methods: isValid() and submit(). Let’s look at the HTML we’ll bind this too and then look at things more closely.

<form data-bind="submit: submit">
    First Name: <input type="text" data-bind="value: firstName" />
    Last Name: <input type="text" data-bind="value: lastName" />
    <input type="submit" data-bind="enable: isValid" />
</form>

Using bindings we’ve bound the two name fields to the text boxes. In addition we now can see what the user’s full name will be as soon as you tab out of either text field. You can also see that we disable the Submit button if either the first name or last name fields are empty. One trick here is that when you reference a ko.observable() field in your object and you want the underlying value, you call your field as a function (as we did in the isValid() method).

If you are interested you can play with the this example here: http://jsfiddle.net/tWM8r/2/

You can find out more about Knockout’s built-in bindings on the website here: http://knockoutjs.com/documentation/introduction.html

Knockout’s bindings will allow you to do most of what you need to do, and when you’re missing something, you can extend Knockout by writing your own custom bindings (something akin to writing a jQuery plugin). We’ll get into custom bindings in a future post.

That’s all for now. Next time we’ll look at Knockout’s template binding feature which is very powerful and useful.

Advertisements

ASP.NET MVC – SortDirection model binder

I’ve recently been working on an ASP.NET MVC 3 project. Yesterday I started working with the WebGrid that comes built-in. One thing that didn’t sit right with me was that the WebGrid posts the sort direction to your controller as either “ASC” or “DESC”, yet in the API when setting up the WebGrid in a view (we’re using Razor) you specify the sort direction using the enum System.Web.UI.WebControls.SortDirection.

I initially tried simply changing the sort direction parameter to be of type SortDirection. This doesn’t work though because the values posted by the WebGrid aren’t string equivalents to the enum values (ie. “Ascending” and “Descending”). So, to fix this problem, I decided to build out a custom model binder. Model Binders in ASP.NET MVC are the pieces that map the data provided by the client (browser) to your controller action parameters.

So here’s the model binder that I came up with.

using System.Web.Mvc;
using System.Web.UI.WebControls;

public class SortDirectionModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (string.IsNullOrEmpty(bindingContext.ModelName))
        {
            return null;
        }

        var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

        if (valueResult == null)
        {
            return null;
        }

        if (valueResult.AttemptedValue.IsNullOrWhiteSpace() || valueResult.AttemptedValue == "ASC")
        {
            return SortDirection.Ascending;
        }
        else
        {
            return SortDirection.Descending;
        }
    }
}

In the model binder we simply grab the posted value and determine if we need to return SortDirection.Ascending or SortDirection.Descending. Once we have the model binder written, we need to let ASP.NET MVC know about it by adding it to the ModelBinders.Binders dictionary. I put this code in the Global.asax file like this:

public class MyApplication : HttpApplication
{
    protected void Application_Start()
    {
        ModelBinders.Binders[System.Web.UI.WebControls.SortDirection] = new SortDirectionModelBinder();
    }
}