Kendo UI + Knockout - custom widget and binding

Demonstrates a custom Kendo UI widget that has a Knockout binding generated for it.

by rniemeyer

HTML

<link rel="stylesheet" href="http://cdn.kendostatic.com/2012.1.229/styles/kendo.common.min.css">
<script src="http://cdn.kendostatic.com/2012.1.322/js/kendo.all.min.js"></script>
<link rel="stylesheet" href="http://cdn.kendostatic.com/2012.2.710/styles/kendo.default.min.css">
<script src="http://knockoutjs.com/js/knockout-2.2.0.js"></script>
<script src="http://rniemeyer.github.com/knockout-kendo/js/knockout-kendo.min.js"></script>
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
<h1>Creating a Knockout binding for a Custom Kendo UI Widget</h1>
<h2>Using just the default option</h2>
<div data-bind="kendoClickToEdit: message"></div>

<hr/>

<h2>Passing multiple options</h2>
<div data-bind="kendoClickToEdit: { value: message, editable: message.editing }"></div>

<hr/>

<div>
   <label class="checkbox">
       <input type="checkbox" data-bind="checked: message.editing" />
       Editable
    </label> 
</div>
<div>
    <input type="text" data-bind="value: message" placeholder="Enter a value...">
</div>

CSS

h1 { font-size: 1.5em; }
h2 { font-size: 1.25em; }

JavaScript

//A sample "ClickToEdit" widget 
(function($) {
   var kendo = window.kendo,
     ui = kendo.ui,
     Widget = ui.Widget;

   var ClickToEdit = Widget.extend({
     init: function(element, options) {
       Widget.fn.init.call(this, element, options);
       this._create();
     },

     options: {
       name: "ClickToEdit",
       value: " ",
       editable: false
     },

     //create the elements for our widget
     _create: function() {
       // setup the link
       var template = kendo.template(this._templates.link);
       this.link = $(template(this.options));
       this.link.click(this._linkClick.bind(this));
         
       // setup the textbox
       template = kendo.template(this._templates.textbox);
       this.textbox = $(template(this.options));
       this.textbox.change(this._inputChange.bind(this));
       this.textbox.blur(this._inputBlur.bind(this));
                
       this._updateVisibility();

       // append all elements to the DOM
       this.element.append(this.link).append(this.textbox);
     },
       
     //public method to update whether it is editable
     allowEditing: function(allow) {
        allow = !!allow; //force to boolean
        if (allow !== this.options.editable) {
            this.options.editable = allow;
            this._updateVisibility(); 
            this.trigger(allow ? "edit" : "view");            
        }       
     },
                         
     //allow programmatic updates to the value
     value: function(newValue) {
         if (newValue !== undefined) {
             if (newValue !== this.options.value) {
                 this.options.value = newValue;
                 this.textbox.val(newValue);
                 this.link.text(newValue);
                 this.trigger("change");
             }                 
         }
         else {
             return this.options.value;
         }                       
     },
       
     //handle the input changing
     _inputChange: function() {
...