Using Durandal and KendoUI together

If you haven’t heard about Durandal you should really check it out. It is a great Single Page Application (SPA) framework written by Rob Eisenberg. You can find more information here.

KendoUI promise to be “Everything you need to build fast HTML 5 apps” – but is mostly about a set of great UI Controls.

For business applications that needs advanced UI controls I think KendoUI and Durandal can be a perfect match.

In theory it is not a problem to combine KendoUI and Durandal, but the KendoUI user controls works best, in my opinion, if you combine it with KendoUI’s own MVVM framework. In that way you can keep your view and viewmodel/model code 100% separated. The problem is that Durandal depends on knockout for databinding (MVVM) – so then there is only two ways to solve this – either you have to make kendoui work together with knockout, or you have to make durandal work together with the kendo databinding framework.

The first option – to make kendo work together with knockout, is actually quite simple as there is already an open source library available that solves this: you can find that library here. But using that library is not a perfect solution, because it is not supported by Kendo. The Kendo support team will refuse to help out with eventual problems if you use the knockout-kendo library – and there is no guaranty that this library will be kept up to date with kendo in the future.

To make Durandal work with the Kendo data-binding framework is probably the best option,but it is a bit harder to do because Durandal depends heavily on knockout. Durandal automatically binds views and viewmodels together using knockout. It would be really nice if we could extend durandal to automatically do this binding also for other data-binding frameworks than knockout. Luckily Durandal is quite extendable, so I was able to come up with the following solution:

I concluded that removing the Durandal’s dependency on knockout was not a good idea so instead of removing the knockout bindings as the default binding I think it would be a good solution to be able to specify that some parts of a view should use another data-binding framework.

Something like this:

<section>
 <h2 data-bind="html: displayName"></h2>
 <form data-mvvm-framework="kendo">
   <label>First Name:<input data-bind="value: firstName" /></label>
   <label>Last Name:<input data-bind="value: lastName" /></label>
 </form>
 <div data-bind="compose: 'rambase/plugins/welcome/viewmodels/welcome'"></div>
</section>

The important part to note here is the “data-mvvm-framework” attribute on the form element. This is an attribute I have added, and the idea here is to tell Durandal to use Kendo data-bindings for the form element, while the rest of the view will use the default knockout data-bindings. It was quite easy to extend Durandal to work like this. The binding between a view and a viewmodel is finding place in a Durandal module called ViewModelBinder. Here the view and viewmodel is bound together using knockout. Luckily Durandal provides two exstension points  (hooks) for data-binding. It has a method called beforeBind and afterBind. By hooking into the beforeBind event I could add my own binding logic like this:


viewModelBinder.beforeBind = function (obj, view) {
    var kendoElements = $(view).find('[data-mvvm-framework=kendo]');
    $(kendoElements).wrapAll('<div data-bind="stopBinding: true"></div>');
    for (var i = 0; i < kendoElements.length; i++) {
       kendo.bind(kendoElements[i], obj.viewModel);
    }
};

This code will find all html elements with a data-mvvm-framework attribute set to kendo. For each of these elements it will bind the element to a viewModel property on the module. I my view there is only one element containing the data-mvvm-framework attribute, but I could have more than one if needed. There is one important line of code in this beforeBind method which needs a bit of an explanation, and that is line 3. Both kendo and knockout uses the data-bind attribute for data-binding, so when using the Kendo data-binding I need to prevent knockout from applying data-bindings on the same element. This line of code will wrap my element in a div which will cause knockout to just leave all child elements alone. When rendered to the browser it will then end up like this:

<section>
 <h2 data-bind="html: displayName"></h2>
 <div data-bind="stopBinding: true">
   <form data-mvvm-framework="kendo">
     <label>First Name:<input data-bind="value: firstName" /></label>
     <label>Last Name:<input data-bind="value: lastName" /></label>
   </form>
 </div>
 <div data-bind="compose: 'rambase/plugins/welcome/viewmodels/welcome'"></div>
</section>

“stopBinding: true” is not something knockout will care about by default, but you can very easily add this custom binding like this:

ko.bindingHandlers.stopBinding = {
   init: function () {
      return { controlsDescendantBindings: true };
   }
};
ko.virtualElements.allowedBindings.stopBinding = true;

You can read more about this trick here: http://www.knockmeout.net/2012/05/quick-tip-skip-binding.html

By using the technique described above you can make Durandal work together with whatever data-binding framework you want – not just Kendo. 

If you don’t care about other data-binding frameworks, and just want Durandal to work as smooth as possible with the Kendo data-bindings the solution can be made simpler. Kendo knows that they are not the only javascript using data- attributes, and thankfully they have made it possible to use a seperate namespace for kendo data- attributes. In that way you can avoid conflicts with other libraries like Knockout (and many others). If you at the startup of you application add this line of code:


kendo.ns = "kendo-";

You will not use the data-bind attribute for data-binding, but instead data-kendo-bind. If I use a kendo namespace like this I don’t have to worry about conflicts anymore and my code can be much easier. I can then change my view code like this:

<section>
 <h2 data-bind="html: displayName"></h2>
 <form>
   <label>First Name:<input data-kendo-bind="value: firstName" /></label>
   <label>Last Name:<input data-kendo-bind="value: lastName" /></label>
 </form>
 <div data-bind="compose: 'rambase/plugins/welcome/viewmodels/welcome'"></div>
</section>

You can see I have removed the data-mvvm-framework attribute as I don’t need it anymore. The first name and last name fields are now bound using kendo, while the h2 element is bound using the default knockout binding. The compose binding will also work as expected, as it will use the knockout compose binding provided by Durandal.

I still have to apply the kendo data-binding in the beforeBind method, but now I can make it much simpler:

viewModelBinder.beforeBind = function (obj, view) {
    kendo.bind(view, obj.viewModel);
};

I now just bind the complete view to the viewModel – there is no need to just bind parts of the view with kendo, because I know that knockout and kendo will not interfere anymore. Now you can use the data-bind and data-kendo-bind together without any problems.

Advertisements

14 thoughts on “Using Durandal and KendoUI together

  1. Thanks for taking the time to write this up, but a little confused with the your last snippet – where you show how you made it much simpler:
    viewModelBinder.beforeBind = function (obj, view) {
    kendo.bind(kendoElements[i], obj.viewModel);
    };

    Shouldn’t that be kendo.bind(view, obj.viewModel) ?

    Reply
  2. Great write-up and i think i’m following all the steps but i’m still not binding w Kendo. Did I put the snippet in the wrong spot? I put it at the end of composition.js:

    viewModelBinder.beforeBind = function (obj, view) {
    kendo.bind(kendoElements[i], obj.viewModel);
    };
    return composition;

    Reply
    • Are you using the kendo namespace technique? If so your code should be

      viewModelBinder.beforeBind = function (obj, view) {
      kendo.bind(view, obj.viewModel);
      };

      If you a using the first teqnique you have to check if your beforeBind function is being used at all. Try setting a breakpoint or something in this function to see if it executes. Do not hesitate to contact me again if you need more help.

      Reply
  3. hi Roger…wow u respond quickly…i’m using the kendo.ns technique:
    In main.js i have:
    kendo.ns = “kendo-“;
    in composition.js i have:
    viewModelBinder.beforeBind = function (obj, view) {
    console.log(“kindobind”);
    kendo.bind(view, obj.viewModel);
    and in my html i have:

    Job Title Kendo:

    Job Title:

    The result is that the kendo isn’t binding but the ko one is. And as you suspected correctly, the beforeBind function does not get called.

    Reply
  4. Hi, I’m trying to figure out where to place :

    viewModelBinder.beforeBind = function (obj, view) {
    kendo.bind(view, obj.viewModel);
    };

    not clear…

    Reply
    • I just updated my git project with a kendo form with this very sample: https://github.com/jstott/breeze-kendo
      See the sample/SPA/HotTowelSpa.sln Build (to get nuget packages) & then run…the tab ‘kendo’ represents a sample form using a kendo.observable.

      The project was started out as a spike to test using the kendo grid with breeze entities – so there is a F:\bitbucket\breeze \Scripts\kendo.breeze.datasource.js in there as well.

      Reply
  5. Some important things for those not getting to this work – some noted in other comments…

    – use the latest version of DurandalJS
    – use the latest version of KendoUI

    Should have been obvious to me!

    Reply

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