Creating custom Knockout Bindings in Durandal

One of the great strengths of Durandal is its integration with KnockoutJS, one of the more popular MVVM libraries for JavaScript available.  The extensibility of Knockout is of particular interest as it allows for custom binds to be written which can do everything from color text based on a condition to initializing JavaScript driven UI components, such as those founds in JQuery UI.

To create these bindings in Durandal we have to take advantage of the plug-in architecture, this starts with a RequireJS module returning an object with an install function.  In our example, I am going to demonstrate creating a specialized enable binding for a Bootstrap Select dropdown list.

Important: Understand that the name of this file will play a huge role later on, so make sure it is named pickerEnable. Furthermore, to date I have not been able to get plug-ins to load outside of putting them in the durandal/js/plugins folder.  I am not aware if this is a requirement for plug-ins or not.

Here is the our skeleton definition:

define(['knockout', 'jquery', 'bootstrap.select'], function(ko, $) {
    return {
        install: function() {
            
        }
    };
});

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Notice that the dependency lists three dependencies but only two variables are passed to the actual module.  Libraries like bootstrap.select do not actually export a central object that we can reference, instead they augment an existing reference ($ in this case).  By indicating bootstrap.select as a dependency the library will be loaded and additional functions made available to the module.

Our next step is to actually add the Knockout binding, to do that we use the familiar custom binding syntax; this becomes straight Knockout at this point.

define(['knockout', 'jquery', 'bootstrap.select'], function(ko, $) {
    return {
        install: function() {
            ko.bindingHandlers.pickerEnable = {
                init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
                },
                update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
                }
            };
        }
    };
});

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

As you might be aware, everything above is optional so we can actually reduce our binding down to only what we absolutely need.  In this case, our binding is very simple so we only need update with a couple of the parameters.  So our binding only looks like this:

define(['knockout', 'jquery', 'bootstrap.select'], function(ko, $) {
    return {
        install: function() {
            ko.bindingHandlers.pickerEnable = {
                update: function(element, valueAccessor) {
                }
            };
        }
    };
});

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Much cleaner. Now we will add the code to actually carry out the binding’s function.  This is purely dependant on what the binding is aiming to accomplish.  In our case, we will examine the value applied to the binding and render the selectpicker accordingly.  Here is our final code:

define(['knockout', 'jquery', 'bootstrap.select'], function(ko, $) {
    return {
        install: function() {
            ko.bindingHandlers.pickerEnable = {
                update: function(element, valueAccessor) {
                    var value = ko.unwrap(valueAccessor());

                    $(element).prop('disabled', !value);
                    $(element).selectpicker('refresh');
                }
            };
        }
    };
});

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

As you can see, all we are doing is unwrapping the value being applied to the binding and adding the disabled flag to our element (a box in this case).  Now, if this were all we had to do we could use one of the standard Knockout bindings.  However, with bootstrap.select an additional call to force the plug-in to re-render the UI is needed, hence our plug-in.

So now we have our code, but we have to actually install it.  To this we need to store the code, as mentioned earlier, in the durandal/js/plugins directory.  The name of the file (minus the .js extension) dictates the name of the plug-in.  In our case, I named the file pickerEnable.js so the plug-in name is pickerEnable.

You can install this plugin along with all of the others.  Within the .js file representing your application (hint, it has your call to app.start()).  One of the other calls usually featured in this file is a call to app.configurePlugins, below is my call which installs the router, http, widget, dialog, and our custom pickerEnable plug-ins.

    app.configurePlugins({
        router: true,
        widget: true,
        dialog: true
        pickerEnable: true
    });

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Notice, we merely pass true and that calls the install function that we defined in the module.  You can see now why the name of the file is important to consider.

So hopefully this has given you some insight into how to create custom Knockout bindings within the Durandal framework.

Advertisements

One thought on “Creating custom Knockout Bindings in Durandal

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s