How Kendo UI Uses Kendo UI To Build Angular Directives For Kendo UI

by Brian Rinaldi on February 3, 2014

The modern web is always changing, and this article is more than two years old.

By Burke Holland

When Telerik first launched the Kendo UI framework as a Beta in November of 2011, we knew that it was going to be very important for any UI widgets we built to integrate easily into other JavaScript frameworks. We saw that code was shifting to the client, and heavy code on the client would necessitate frameworks to help developers deal with the complexity. We wanted Kendo UI to “just work” right alongside these libraries.

At that time (late 2011), the dominant JavaScript framework was Backbone. We based this on what we saw online in terms of items like our UserVoice portal, StackOverflow and our forums. We even created a set of demos for our site showing Kendo UI working alongside of Backbone.

During the first part of 2012, we also added declarative initialization to Kendo UI. This was something that one of the other project teams – now known as Telerik AppBuilder – had requested that we do. Telerik uses it’s own tools to build other tools, and we discovered in the process of doing so that a declarative API faciliated easier maintenace and structuring of large applications. This allowed the developer to initialize a widget by passing the widget type and all configuration values via data attributes. This meant that if you wanted an AutoComplete, you could create it like so…

<input data-role="autocomplete"
       data-text-field="text"
       data-value-field="value"
       data-source="things">

<script>
  var ds = new kendo.data.DataSource({
    data: [ { text: "Thing 1", value: 0 }, 
            { text: "Thing 2", value: 1 },
            { text: "Thing 3", value: 2 },
            { text: "Thing 4", value: 3 }]
  });

  kendo.init(document.body);
</script>

Which would render…

autcomplete

As 2012 began to unfold, we saw a sharp drift from Backbone to Knockout.

2012

What’s interesting is that these two libraries solve rather different problems, but don’t necessarily play very nicely together. It was during this wave of popularity for Knockout that we worked with the Knockout community to create the open source Knockout Kendo UI project. The custom bindings were meant to allow developers to use Kendo UI widgets with Knockout and get the same benefit of the two-way binding that an MVVM pattern provides.

This also lead us to create our own implementation of MVVM. The reason why we did not build on top of Knockout was to avoid a tight coupling with a framework over which we had no control. As Kendo UI has an aggressive release cycle, it’s much easier to iterate without dependencies.

As we moved into 2013, the strong Knockout trend continued. In the early part of the spring, we saw Angular begin to appear in our feedback channels. In the early part of the summer, AngularJS absolutely exploded and seemed to dominate every framework discussion we were having.

2013

It was during this time that our Product Manager for Kendo UI (Brandon Satrom) asked me to do some research into adding Angular integrations for Kendo UI into the Kendo UI labs; and with that simple email, Angular Kendo UI was born.

angularkendoui

Angular Kendo UI

When I initially began looking at Angular, everything worked so wonderfully. Bindings were easy and clear. The scope was simple to understand and the controller provided a nice pattern for partials. Then I hit directives and the honeymoon screached to a halt. If you’ve done any work with Angular, you know exactly what I’m talking about. Directives are easily the hardest part of Angular to understand, and yet they are imperative for building reusable UI components. Since directives are the only place that Angular offers a reference to the DOM, building integrations for Angular was going to require me to write some directives.

Like all the great developers, I have very few original ideas. So I looked to the community for inspiration.

Learning From Angular UI

I knew that somebody somewhere had to be building integrations for jQuery UI. As it turns out, there was an Angular UI project that was doing just that. They were building directives for Bootstrap as well as a few other UI components and already had a structure in place for me to examine. This is a screenshot of the Angular UI Bootstrap directives repo folder.

angularui

You can see that each component or widget has it’s own folder. Inside each folder is a directive, a template (if necessary) and a corresponding test. I then went back to the Angular Kendo UI integrations and assessed the situation with my new found knowledge of how others were attacking the same issue. Things were not good.

Kendo UI is an agressive yet modular UI framework. It has three distinct parts – Web, DataViz and Mobile. A developer can use just what they need, up to and including any of the over 50 widgets that are available. On top of that, I knew what the roadmap was for Kendo UI. I knew that new widgets were coming and there were going to be a lot of them. This was momentum that would not be slowing down.

I was staring down the barrel of having to create over 44 directives and I was scared. I’m only one developer and I’m not even that good. I also have other things to do during the day besides writing and maintaining directives. We would need an entire team of developers to write integrations if we created a directive for every widget. Instead, I thought I would get smart.

Nailed It

Since Kendo UI has a declarative interface, I discerned that I could provide a sneaky pass-through, handle the whole thing in one directive and be done with it. Which is exactly what I did.

angular.module('kendo-directives', []).directive('kendo', [ function() {
  return {
    link: function(scope, element) {
      kendo.bind(element, scope)
    }
  }
}]);

Barely able to contain my anticipation, I wired up my new directive, dropped a ‘kendo’ attribute on my existing HTML

<input kendo data-role="autocomplete"
       data-text-field="text"
       data-value-field="value"
       data-source="things">

And wouldn’t you know it, out the other end I got…

autcomplete

I immediately looked around to see if anyone had watched me just perform this feat of programming genius. Had I really just created an entire set of integrations for Kendo UI with 8 lines of code? I was expecting my phone to begin ringing as corporate VP’s at Apple and Google clamored to have me join their boards. Surely this was one of the greatest computing achievement of my generation.

Sadly, the Angular community quickly pointed out that while this technically does render an AutoComplete, that’s about all it does. Now normally this would be enough, but in Angular, the stakes are much, much higher. Angular developers expect far more than a just a UI widget.

Angular’s Raised Expectations

The problem was that the widget was completely out of the Angular binding pipeline. If you bound the value of the widget to the scope, changing the widget value did not update the scope variable and vice versa. Back to the drawing board. I took another look at Angular UI and still could not emotionally center myself on having to write one directive for every widget.

At this point, some very bright Angular folks had joined the project. Pierre Asselin and Omkar Patil were using AngularJS daily with Kendo UI and had begun to offer me some guidance on how we might proceed. They were the first members of the core team. Pierre and I then went about planning how we could build directives for Kendo UI that would scale along with the framework without us having to constantly work to keep up. We decided the best way to do this was not to build directives.

Don’t Build Directives

Directives are hard. They are very complex and since you are dealing with the UI, you have to worry about things like what directives are already on an element, in what order do they execute, how to communicate changes in the DOM with Angular’s scope and vice versa. Not to mention that you end up with quite a bit of boilerplate code typing things like “transclude” and “restrict” over and over again.

Since we didn’t want to build all these directives, we decided it would be easier if we let Kendo UI and Angular build them for us.

Generating Directives

This probably sounds like snake oil, so lets talk it through…

Widgets Share A Common API

Kendo UI has the benefit of being a very focused commercial product. This means that its core API is very well thought out and architected. It also means that every single one of the widgets are built on the same set of core widgets. The core widgets, such as the list widget and the popup widget, are not exposed as actual widgets in the framework, but are used as a base that other widgets (like the AutoComplete) can then extend. This means that all widgets share a very common API.

It’s Mostly About CHANGE And VALUE

When you are trying to keep Angular in sync with what’s happening in a widget, you are primarily concerned with whatever the widget’s current value is and whether or not it has changed. Conversely, you need to be able to update that value if and when the scope changes.

We Already Know Which Widgets The Developer Wants To Use

Kendo UI, much like jQuery UI or any jQuery based JavaScript UI, stores it’s widgets off of a namespace (in this case kendo.ui and kendo.dataviz.ui). This means that we could inspect that namespace to know which widgets the user has included in the page.

kendonamespace

Given these facts, we actually have enough information to have a single directive that creates these other directives. The algorithm sort of flows like this…

For each widget in the kendo.ui/kendo.dataviz.ui namespaces:

  • Create a directive
    • Merge the attributes (we used k-) into a single object
    • Bind to the change event and apply changes to the scope
    • Use ngModel to watch for changes on the scope, setting the widget value
    • In the directive “link”
      • Initialize the widget with the object built up from the attributes

What results is a directive for each widget, created from Kendo UI using Angular.

Is This Even A Good Idea?

While this looks very promising on the surface, we have to look at its long term viability. Specifically – will it scale? Will this work with all widgets and cover all use cases? Or will it break down under the weight of a growing feature set.

It has several very immediate and tangible benefits.

DRY

This is very, very DRY code. Literally the same block is executed over and over again, creating a different directive each time.

No Bundling

This negates the need for us to bundle anything for the developer. Should they only need the AutoComplete directive, all they have to do is just include the AutoComplete from Kendo UI. Should they include all of the widgets, the same code scales to cover the required surface area.

No Boilerplate

Inside of every developer, there is a still small mechanisms that will fire whenever it detects any sort of repetition. It will say very firmly, “You need to automate that bro.” We do not like boilerplate and ceremony. It’s extraneous and hard on the eyes. Having one directive eleminates all of the boilerplate code that goes into creating and structuring a plethora of different directives.

Drawbacks

Like anything that looks good on the surface, it’s really blood and roses. There are some potentially serious drawbacks to this architecture.

Brittle

Since there is one directive building the others, if you break that one directive, you have broken them all. The code is extremely brittle. We usually try to avoid this in application architecture and we know that having more files means a heavier maintenance burden, but encapsulation also prevents breaking changes from seeping throughout the codebase. In this case, we have built a factory that is now building directives. If something goes wrong in our factory, our directives will suffer. The user is going to report a problem with the product, not the factory. This could make bug reports and issues rather tricky to track back to their point of origination.

Complex Widgets Are Complex

So far I’ve talked about how Angluar is mostly concerned with just the change and value of the widget. While this is mostly true, some widgets are very complex and have needs that go way past just change and value. A good example is the Grid widget. The grid is a full CRUD surface that can almost contain an entire application’s functionality. This means that there are a lot more things going on that we need to be able to handle. Actions in the grid must correspond to their values on the scope. Grid rows are repeated and have to be compiled by Angular as developers are going to expect that if they pass in an expression, it will be properly evaluated.

Currently, we address this by providing a hash object for these complex widgets. When the widget is generated, we check the hash and then wire up this additional functionality as specified by the hash. Right now the footprint of this object is incredibly small, but in the future, this hash object should ideally be split out into separate modules so that the developer doesn’t have to include unecessary code.

Does It Really Scale?

This is the million dollar question. Since the library is so monolithic, the question becomes whether or not we can actually scale it to integrate enough of Kendo UI’s features into AngularJS to be able to say that we “fully support” Angular. If the foundation which generates the directives isn’t flexible enough, the entire library will crumble under the weight of the Kendo UI framework. In truth, only time will tell if this is the case. As Tim Berner’s Lee once said, “I think when you have a lot of jumbled up ideas, they come together slowly over a period of time”.

So far we are looking very good, and assuming that we continue down this path, there are a few other things we can do to further facilitate this “directive generation” idea.

Teach Directives With Metadata

The idea is that you have some widgets that need extra information in order to work correctly with Angular. Let’s take for example the NumericTextBox. This widget has a change event just like all the other widgets, and it fires on blur. It also has a spin event which is also a change. This is the event which fires whenever the user clicks the up or down arrow on the right of the widget. Angular needs to know about both of these change events. Right now the way we do this is with a hash lookup.

var notify = {
  All: [ "change" ],
  NumericTextBox: [ "spin" ]
}

All widgets will iterate the All property and bind to those events. Then they will check to see if there are any special events they need to notify Angular of. In this case, the NumericTextBox has a spin event that it needs to bind to as well so it can be applied to the scope.

While this is good and works just fine, we are contemplating making this part of widget metadata.

Why Metadata

We actually provide metadata in our widgets already. The Kendo UI Bootstrapper will parse your application, determine which widgets you are using and bundle your Kendo UI JavaScript for you. To do this, it needs to know about widget dependencies. We provide this information on each widget in the form of metadata. This means we can provide a build tool client-side that will always work, even if dependencies differ between versions of Kendo UI. Currently, the header of a Kendo UI widget (in this case, the NumericTextBox) looks like this:

var __meta__ = {
  id: "numerictextbox",
  name: "NumericTextBox",
  category: "web",
  description: "The NumericTextBox widget can format and display numeric, percentage or currency textbox.",
  depends: [ "core", "userevents" ]
};

We could then add a notify property to this object that would let us know if this widget needs to keep Angular in the loop on any events other than just change.

var __meta__ = {
  id: "numerictextbox",
  name: "NumericTextBox",
  category: "web",
  description: "The NumericTextBox widget can format and display numeric, percentage or currency textbox.",
  depends: [ "core", "userevents" ]
  notify: [ "spin" ]
};

This would keep us from having to maintain the lookup in the Angular directive. Again, this is all about scale. How can we leverage Kendo UI to build Angular JS directives that won’t break when the library changes, and can grow with it requiring minimal human intervention.

Never Go Full Angular

Working on this project taught me one valuable lesson about coding to a framework.

simplejack

Angular introduces a lot of structure for the JavaScript developer. We have existed without proper modules and code containers for a long time in the web development world. We’ve come up with all sorts of creative ways of mimicking these shortcomings using patterns like “Revealing Module” and “Module Export”. The fact that Angular gives us constructs for these items (modules, services, factories) and then automatically injects them as dependencies is very appealing. No more guessing or trying to implement the “best” pattern; you just implement the Angular one. The problem with this is that you can take it too far and forget that plain JavaScript concepts still work in Angular. This is exactly what happened to me.

In the case of interating over the Kendo UI namespaces, I intially created a service that would run this iteration and then provide the array of widgets back to the directive. I wrote it as a service.

angular.module('kendo.directives', [], ['$provide',

  function($provide){

    var widgets = [],
        namespaces = [ kendo.ui, kendo.dataviz ];

     // iterate over the namespaces
      angular.forEach(namespaces,  function(namespace) {

        ...code omitted for berevity

      });

    });

    $provide.value('kendoWidgets', widgets);

}]);

As it turns out, all I’m doing here is building up an array. Which means that – believe it or not – the following code also does the exact same thing and is still testable…

var widgets = (function() {

  var widgets = [],
      namespaces = [ kendo.ui, kendo.dataviz ];

   // iterate over the namespaces
    angular.forEach(namespaces,  function(namespace) {

      ...code omitted for berevity

    });

  });

  return widgets;

}());

This is not to suggest that you shouldn’t use Angular’s constructs. It’s just that you shouldn’t forget that JavaScript still works with Angular. I promise. Simple code is always better code.

Angular Kendo UI

If you haven’t checked out Angular Kendo UI, I encourage you to hit up our repo and let us know what you think. Got suggestions? Concerns? See something that we could do better? Please by all means let us know. Angular is still relatively new, and we’re all navigating the rough waters trying to figure out the best way to take what we already have and determine if Angular adds in the necessary structure on which to stabalize our applications.

You can watch the talk that inspired this post. You can actually watch all of the sessions from ng-conf. I would highly recommend watching the keynote with Brad and Misko which goes over the future of Angular and 2.0. With 14 full time people working on the framework, it’s an exciting time to be an Angular developer.

Art by Urs Schmid

5 comments"

  1. Gaurav says:

    Question: Is there some way i can check how the kendo directives are implemented ?

  2. gaurav says:

    How can we check as how the kendo directive is implemented ? I want to make kendo type of widgets but do not have clear idea

  3. Justin says:

    Unfortunately, your idea of having one directive for all the controls might work for most of the widgets. But the complex widgets like the grid deserve their own directives. Do you realize that a watcher is created for every cell in the grid? What this equates to is horrific performance and makes the grid unacceptable slow. A simple 20 column grid with 1000 rows equals 20k watchers. The creators of Angular suggest no more than 2k watchers at any one time. In deed, this pure Angular grid (http://www.ag-grid.com) has no watchers and performs incredible fast. Please rethink this approach and give us a better grid directive.

  4. Scott Hendersson says:

    Hi!
    Great howto!
    I think you should make a howto about http://www.aquro.com as well! They have a lot of smart functionality.

    Regards,
    Scott

Leave a Reply

Your email address will not be published. Required fields are marked *

© 2016 Modern Web & our authors. All rights reserved.