× {{alert.msg}} Never ask again
Receive New Tutorials
GET IT FREE

Using Plain HTML to Develop Web Widgets with Kendo UI

– {{showDate(postTime)}}

Codementor Javascript Expert Burke Holland works for Telerik has been working on Kendo UI, a popular HTML5, jQuery-based tool for building modern web apps, since before its release. He is the original author of Angular Kendo UI, and he works on educating developers on how to build mobile apps with Kendo UI.

This article was originally posted on his blog.


You already know that Kendo UI has magnificent widgets. UI is in the name, and Telerik has a long proud history of building amazing UI components. What you may not know, is that you can actually use Kendo UI (specifically MVVM) to work with plain HTML to construct your own widgets. This is sort of like running on the bare metal, but it’s tons of fun and thanks to Kendo UI’s streamlined MVVM, it’s quite easy as well.

Looping: How Rumors Get Started

Knockout provides the ability to loop over a collection by implementing a for iterator. The power of this really can’t be underestimated since it allows you to take collections and render them visually with HTML getting fine grained control over how each of the items will look. The only problem with this, is that for loops can get unwieldy and don’t mix very gracefully in HTML attributes. This earned some bad rep for Knockout in it’s early days through no fault of the framework itself. Regardless of it’s tendency to clutter HTML, it’s a powerful construct that is noticeably absent from Kendo UI’s MVVM implementation. Or is it?

Kendo UI will actually gladly loop over any collection and construct the contents based on HTML. You don’t need a widget to do this. Honestly! Kendo UI will take any element bound to a valid collection, and as long as a template has been specified, it will iterate over the collection appending a new item composed from the template for each item in the collection. That’s a lot of words, so let me show you a simple example.

HTML

<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdn.kendostatic.com/2013.3.1119/js/kendo.all.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
  
  <ul data-bind="source: things" data-template="things-template"></ul>
  
  <script type="x-kendo-template" id="things-template">
    <li data-bind="html: data"></li>
  </script>
  
</body>
</html>

Javascript

(function() {

  var o = kendo.observable({
    things: [ 'Thing 1', 'Thing 2' ]
  });
  
  kendo.bind(document.body, o);

}());

There is a ul with a data-source set to an array on a Kendo UI Observable. The source array things contains only strings. We can repeat each one of these items and specify how it should look by using a template. Since there are no objects in the array and only strings, specifying data is sufficient to display the value. If you’ve been working with Knockout or Angular, your very next thought is probably “If I update the the array, does the HTML update too?” Let’s find out.

HTML

<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdn.kendostatic.com/2013.3.1119/js/kendo.all.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
  
  <ul data-bind="source: things" data-template="things-template"></ul>
  
  <input class data-bind="value: value" />
  <button class="btn" data-bind="click: add">Add</button>
  
  <script type="x-kendo-template" id="things-template">
    <li class="list-group-item" data-bind="html: data"></li>
  </script>
  
</body>
</html>

Javascript

(function() {

  var o = kendo.observable({
    things: [],
    value: null,
    add: function(e) {
      var array = this.get('things');
      var value = this.get('value');
    
      array.push(value);
      console.log(value);
    }
  });
  
  kendo.bind(document.body, o);

}());

The array on the Kendo UI Observable gets wrapped as an ObservableArray automatically. This means that we can work with it’s items and Kendo UI keeps the HTML in the loop. So far this is interesting, but not really impressive. Let’s build a more complex example and work with the existing items themselves adding some more in-depth markup. Let’s tweak this example so that we can edit each individual item to find out how detailed our bindings currently are.

As it turns out, each event that gets dispatched from a Kendo UI MVVM binding includes the associated data item if there is one. Lets turn to the over-used Todo list example to see this in action. Then we’ll walk through the code. The following example contains no widgets and is almost entirely powered by the keyboard. Enter a new item and press enter to save it. Double-click an item to edit it, and click enter to save your edit.

HTML

<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery.min.js"></script>
<link href="http://getbootstrap.com/dist/css/bootstrap.css" rel="stylesheet" type="text/css" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdn.kendostatic.com/2013.3.1119/js/kendo.web.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
  
  <div class="container">
    <div class="row">
      <div class="col-xs-10 col-xs-offset-1">
        <input type="text" class="form-control" data-bind="events: { keypress: add }" />
      </div>
    </div>
    <div class="row">
      <div class="col-xs-10 col-xs-offset-1">
        <ul class="list-group" data-bind="source: things" data-template="things-template"></ul>      
      </div>
    </div>
  </div>
  
  <script type="x-kendo-template" id="things-template">
    <li data-bind="events: { dblclick: edit }" class="list-group-item">
      <span data-bind="html: title, invisible: editing"></span>
      <input type="text" class="form-control edit-box" data-bind="visible: editing, events: { blur: save, change: save, keypress: enter }, value: title">
    </li>
  </script>
  
</body>
</html>

Javascript

(function() {
  
  // constant for enter key code
  var ENTER = 13;
  
  // datasource starts empty and defines schema
  var things = new kendo.data.DataSource({
    schema: {
      model: {
        fields: [
          { title: "title" }
        ]
      }
    }
  });

  var o = kendo.observable({
    
    things: things,
    
    // adds a new item to the datasource
    add: function (e) {
      var item = $(e.target);
      var value = item.val().trim();
      
      // if the enter key was the key pressed, and 
      // the value entered is not whitespace or blank
      if (e.which === ENTER && value.length) {
        // add the value to the datasource
        things.add({ title: value });  
        item.val('');
      }
    },
    edit: function (e) {
      e.data.set('editing', true);
      $(e.target).next().focus();
    },
    enter: function(e) {
      if (e.which === ENTER) {
        this.get('save')(e);  
      }
    },
    save: function(e) {
      console.log('save');
      if (e.data.get('title').trim().length) {
          e.data.set('editing', false);
      }
      else {
        this.get('things').remove(e.data);
      }
    }
  });
  
  kendo.bind(document.body, o);

}());

First look at the HTML that goes into constructing this example.

<div class="container">
  <div class="row">
    <div class="col-xs-10 col-xs-offset-1">
      <input type="text" class="form-control" data-bind="events: { keypress: add }" />
    </div>
  </div>
  <div class="row">
    <div class="col-xs-10 col-xs-offset-1">
      <ul class="list-group" data-bind="source: things" data-template="things-template"></ul>      
    </div>
  </div>
</div>

<script type="x-kendo-template" id="things-template">
  <li data-bind="events: { dblclick: edit }" class="list-group-item">
    <span data-bind="html: title, invisible: editing"></span>
    <input type="text" class="form-control" 
      data-bind="visible: editing, events: { blur: save, keypress: enter }, value: title">
  </li>
</script>

If you look through the HTML (there isn’t much of it), you will see a few interesting bindings; notably the keypress and dblclick bindings. You may not have used these bindings before, but they are incredibly powerful. In this example, they allow us to interact with the application without adding any buttons. Another interesting feature is a subtle one in the template. Each item in the template is going to be paired with an item from the collection in JavaScript. The span holds the viewable value while the input holds the editable one. Both are bound to the same value. But notice that the events like dblclick and blur are bound to events as well. If the title property exists on each data item, do the functions edit, save and enter need to exist there too? Lets look at the JavaScript and find out…

(function() {

  // constant for enter key code
  var ENTER = 13;

  // datasource starts empty and defines schema
  var things = new kendo.data.DataSource({
    schema: {
      model: {
        fields: [
          { title: "title" }
        ]
      }
    }
  });

  var o = kendo.observable({

    things: things,

    add: function (e) {

      var item = $(e.target);
      var value = item.val().trim();

      // if the enter key was the key pressed, and 
      // the value entered is not whitespace or blank
      if (e.which === ENTER && value.length) {

        // add the value to the datasource
        things.add({ title: value });  

        // rest the input value
        item.val('');
      }
    },

    edit: function (e) {

      e.data.set('editing', true);

      // set the focus so the user doesn't have to do 
      // it manually
      $(e.target).next().focus();
    },

    enter: function(e) {

      // if the key pressed was the enter key
      if (e.which === ENTER) {

        // call the save function
        this.get('save')(e);  

      }
    },

    save: function(e) {

      // if the item has a value
      if (e.data.get('title').trim().length) {

        // toggle out of edit mode
        e.data.set('editing', false);

      } 
      else {

        // its blank or whitespace so just remove it
        this.get('things').remove(e.data);

      }
    }

  });

  kendo.bind(document.body, o);

}());

The events actually exist on the main view model, not each data item. However, Kendo UI is smart enough to map these bindings up the tree to find the matching handler. This is really nice because while we want access to each individual item from the collection in the template, we also want access to the root view model which is what Kendo UI provides us. Further, each bubbled up event actually contains the data item from the element on which it originated via e.data. Also notice that there are virtually no jQuery selectors in this code. We are almost entirely concerned with updating the state of the model, which in turn controls the state of the HTML. This makes the code extremely portable. You could literally pick this up (HTML and JavaScript) and drop it on another page and it would “just work”.

Inheriting To Encapsulate

What if we wanted two of these lists on a page? If you duplicated the above HTML (not including the template) and bound the whole document to the same view model, you would get two lists. However, they would always display identical data. Whatever action you took in one would apply to the other. You probably wouldn’t want this. To make a reusable component, we can encapsulate all of our login in a class. Since JavaScript doesn’t have classes, you would normally have to do some wizardry to pull this off. Kendo UI does this black magic for you via the kendo.Class.extend method so you can just work with classes as if they actually exist in JavaScript.

HTML

<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery.min.js"></script>
<link href="http://getbootstrap.com/dist/css/bootstrap.css" rel="stylesheet" type="text/css" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdn.kendostatic.com/2013.3.1119/js/kendo.web.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
  
  <div class="container">
    
    <div id="list1" class="col-xs-6">
      <div class="row">
        <div class="col-xs-10 col-xs-offset-1">
          <input type="text" class="form-control" data-bind="events: { keypress: add }" />
        </div>
      </div>
      <div class="row">
        <div class="col-xs-10 col-xs-offset-1">
          <ul class="list-group" data-bind="source: things" data-template="things-template"></ul>      
        </div>
      </div>  
    </div>
    
    <div id="list2" class="col-xs-6">
      <div class="row">
        <div class="col-xs-10 col-xs-offset-1">
          <input type="text" class="form-control" data-bind="events: { keypress: add }" />
        </div>
      </div>
      <div class="row">
        <div class="col-xs-10 col-xs-offset-1">
          <ul class="list-group" data-bind="source: things" data-template="things-template"></ul>      
        </div>
      </div>
    </div>
    
  </div>
 
  
  <script type="x-kendo-template" id="things-template">
    <li data-bind="events: { dblclick: edit }" class="list-group-item">
      <span data-bind="html: title, invisible: editing"></span>
      <input type="text" class="form-control edit-box" data-bind="visible: editing, events: { blur: save, change: save, keypress: enter }, value: title">
    </li>
  </script>
  
</body>
</html>

Javascript

(function() {
  
  // create a base class
  var Todo = kendo.Class.extend({
    
    init: function (element) {
      
      // constant for enter key code
      var ENTER = 13;
      
      console.log('new ds!');
      
      // datasource starts empty and defines schema
      var things = new kendo.data.DataSource({
        schema: {
          model: {
            fields: [
              { title: "title" }
            ]
          }
        }
      });
      
      // viewmodel
      var viewModel = kendo.observable({
    
        things: things,
        
        add: function (e) {
      
          var item = $(e.target);
          var value = item.val().trim();
      
          // if the enter key was the key pressed, and 
          // the value entered is not whitespace or blank
          if (e.which === ENTER && value.length) {
        
          // add the value to the datasource
          things.add({ title: value });  
        
          // rest the input value
          item.val('');
        }
      },
    
      edit: function (e) {
      
        e.data.set('editing', true);
      
        // set the focus so the user doesn't have to do 
        // it manually
        $(e.target).next().focus();
      },

      enter: function (e) {
       
        // if the key pressed was the enter key
        if (e.which === ENTER) {
        
          // call the save function
          this.get('save')(e);  
      
        }
      },
    
      save: function (e) {
      
        // if the item has a value
        if (e.data.get('title').trim().length) {
          
          // toggle out of edit mode
          e.data.set('editing', false);
      
        } 
        else {
        
          // its blank or whitespace so just remove it
          things.remove(e.data);
      
        }
      }
    });
      
    kendo.bind($(element), viewModel);
  }
});
  
  var list1 = new Todo("#list1");
  var list2 = new Todo("#list2");

}());

Now a new todo list can be created by including the markup required to generate the component and creating a new Todo, which will create new instances of the datasource and the necessary view models. The template can be shared between both without issue.

Immediate Updates

One last issue that I wanted to touch on was a slight misunderstanding that I see from time to time regarding Angular. Angular is a fantastic framework. We think it’s well done and that’s why we are working hard on Kendo UI directives for Angular. I do sometimes hear folks say, “yes, but Angular updates AS YOU TYPE WHICH IS AMAZING!”. It is pretty neat isn’t it? Turns out, Kendo UI does this too.

Kendo UI will by default wait until a field blurs before it syncs changes. This is because it’s more efficient to wait until an edit is finished than to check every time you hit a key. Less checks are faster than more. That’s just common sense. You can override this behavior by using the data-value-update attribute on the element that you want to update the binding on when a key is pressed.

<input type="text" data-bind="value: value" data-value-update="keyup">
<span data-bind="html: value"></span>

<script>

  (function() {

    var o = kendo.observable({
      value: null
    });

    kendo.bind(document.body, o);

  }());

</script>

HTML

<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery.min.js"></script>
<link href="http://getbootstrap.com/dist/css/bootstrap.css" rel="stylesheet" type="text/css" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdn.kendostatic.com/2013.3.1119/js/kendo.web.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
  
  <div class="container">
    <div class="col-xs-12">
      <input class="form-control" data-value-update="keyup" data-bind="value: text">
    </div>
    <div class="col-xs-12">
      <h3 data-bind="html: text"></h3>
    </div>
  </div>
      
  
</body>
</html>

Javascript

(function() {
  
  var o = kendo.observable({
     text: null
  });
    
  kendo.bind(document.body, o);
  
}());

Which Framework Should I Use

People often ask me, “Which framework should I use with Kendo UI?”. If you’ve made it this far into the article, you can probably guess what my answer is; Kendo UI. You should be able to use your favorite JavaScript framework and Kendo UI widgets will work right along with it. That might be via the Knockout Kendo UI or Angular Kendo UI projects. However, if you haven’t yet chosen a framework, and you think you need to to structure your code, I urge you to think again. Kendo UI provides a full stack for developing rich desktop and mobile SPA’s and applications with JavaScript. Not only do you get the best UI widgets available, but they come with a powerful framework that has all of the features that you might find in other MV* frameworks.

Full Todo List Example

I’ve also just updated the Kendo UI TodoMVC example to include the new Router component in the Kendo UI framework. Make sure you check that project out for a really in-depth look at what you can make the framework do without any widgets at all.


Burke HollandNeed Burke’s help? Book a 1-on-1 session!

View Berke’s Profile

or join us as an expert mentor!



Author
Burke Holland
Burke Holland
5.0
I'm a web developer working for Telerik on the latest and greatest web and mobile technologies. I know what it's like to be stuck. When you can't see the forest through the trees. When you want...
Hire the Author

Questions about this tutorial?  Get Live 1:1 help from Kendo UI experts!
Sumit S
Sumit S
5.0
Senior full stack developer
More than 10 years of experience in web/product development(worked at Microsoft for 10 years). Currently working as a Full-stack developer....
Hire this Expert
Adam Barani
Adam Barani
5.0
Technical Lead Software Engineer | Ex Bootcamp Instructor | Consultant
More than 9 years of experience coding for big companies in the US. Mastery level in JavaScript and MERN stack (Mongo, Express, React, Node JS)....
Hire this Expert
comments powered by Disqus