(or, Adding / Removing CSS Classes with a Custom Kendo MVVM Binder)

Custom MVVM binders are a powerful part of the Kendo UI MVVM framework. Making basic custom binders isn’t too difficult. There is an example of making a custom binding on the Kendo UI Demos page.

However, you have likely used the built-in Kendo bindings that use complex binding paths or objects. For example, the events and attr bindings:

<div data-bind="events: { click: onClick, mouseover: onMouseOver, mouseout: onMouseOut }">

If you have tried to make a binder like this yourself, you have probably run into issues. Kendo actually does not provide support for these complex bind paths for custom binders. The ones that are built into Kendo are handled differently internally, letting them work correctly. However, there is a way we can make them work with a little extra effort.

For this example, we are going to make a custom binder that adds/removes CSS classes from the target element. This will be done using jQuery’s .addClass() and .removeClass() functions so that it will not clobber any existing classes defined on the element. Our desired syntax will be:

<div data-bind="class: { name-of-class: value, name-of-another-class: value, ... }">

So the left side of the colon will be the name of the class, and the right side will be the value to check. If the value is truthy the class will be added. If falsey the class will be removed.

We can start by defining the basic code for the custom binder:

kendo.data.binders.class = kendo.data.Binder.extend({
  init: function (target, bindings, options) {
    kendo.data.Binder.fn.init.call(this, target, bindings, options);
  },

  refresh: function () {
  }
});

So, the big reason that Kendo doesn't appear to play nicely with custom complex bindings is that in the refresh function you will want to call: this.bindings.class.get() to get the value of the binding path (the part on the right of the colon). However, Kendo expects this to be a string representation of a variable or function name. So if our custom binding was simply data-bind="class: getClassName" then the call to .get() would try to find the value of a property or function named "getClassName" however in our complex example, the .get() function doesn’t know how to return a value for "{ name-of-class: value, name-of-another-class: value }" because it doesn't define a single path to a single piece of data.

What we need to do is actually call this.bindings.class.get() multiple times, one for each of the properties (class names) of our complex binding type. The .get() function looks in the binding object’s path property to get this path string. We just need to set it each time.

OK, so, to do this, we need to start by adding some code to our binder’s .init() constructor function to look at our complex binding path, and get a list of all the ‘keys’ (the class names).

kendo.data.binders.class = kendo.data.Binder.extend({
  init: function (target, bindings, options) {
      kendo.data.Binder.fn.init.call(this, target, bindings, options);

      // get list of class names from our complex binding path object
      this._lookups = [];
      for (var key in this.bindings.class.path) {
          this._lookups.push({
              key: key,
              path: this.bindings.class.path[key]
          });
      }
  },

  refresh: function () {
  }
});

So if your example binding HTML is:

<div data-bind="class: { selected: isSelected, error: hasError }">

then what we just built and stored in _lookups is:

[
  { key: selected, path: isSelected },
  { key: error, path: hasError }
]

Now in our .refresh() function, we want to loop over the items in our _lookups array. For each we will set the binder’s path to the path from the lookup item, and call the binder’s .get() function. Then we can act on each of them.

kendo.data.binders.class = kendo.data.Binder.extend({
  init: function (target, bindings, options) {
    kendo.data.Binder.fn.init.call(this, target, bindings, options);

    // get list of class names from our complex binding path object
    this._lookups = [];
    for (var key in this.bindings.class.path) {
      this._lookups.push({
        key: key,
        path: this.bindings.class.path[key]
      });
    }
  },

  refresh: function () {
    var lookup,
    value;

    for (var i = 0; i < this._lookups.length; i++) {
      lookup = this._lookups[i];

      // set the binder's path to the one for this lookup,
      // because this is what .get() acts on.
      this.bindings.class.path = lookup.path;
      value = this.bindings.class.get();

      // add or remove CSS class based on if value is truthy
      if (value) {
        $(this.element).addClass(lookup.key);
      } else {
        $(this.element).removeClass(lookup.key);
      }
    }
  }
});

So there we have it! A fully functional, very useful custom Kendo binder that uses a complex binding path!  Download Kendo UI and discover the power of the MVVM binding framework in your web and mobile applications.


About the Author

Jeff Valore

Jeff Valore enjoys promoting Software Craftsmanship at local user groups, and focusing on Microsoft technologies and the .NET stack. You can follow Jeff on Twitter @CodingWithSpike

Related Posts

Comments

Comments are disabled in preview mode.