Can do with Kendo - The Master Detail

July 13, 2015

The increasing maturity of native JavaScript implementation in modern browsers has made it the 'Lingua Franca' of the Web. Practically all modern day web sites and web applications rely on JavaScript to deliver robust interactivity and features. Though JavaScript has existed in browsers since the earliest days, its popularity amongst web developers exploded with the advent of libraries and frameworks that smooth out the difficulties in writing cross-browser compatible applications utilizing JavaScript. JQuery being one of the most popular browser DOM manipulation library, provided the foundation for many other purpose-built libraries such as jQuery UI for user interface widgets and DataJS for data centric web application utilizing JSON and OData. There are other libraries such as AngularJS, KnockoutJS that bring the concepts of pattern-based software application development to the world of web applications and still others such as QUnitJS that supply the unit testing framework to round out the landscape of tools available for creating feature rich, efficient, high quality web applications.

While most of these libraries are available as open source software, there are some fabulous vendor implementations that bring these concepts together into a powerful and modern web application development toolset.

KENDO UI

The focus of this series is on Telerik's Kendo UI, which is one such framework with 70+ jQuery based UI widgets, AngularJS integration, Bootstrap support, mobile controls and offline data solutions for building web and mobile applications with HTML5 and JavaScript. It is available as Kendo UI Core under the Apache v2 open source license and Kendo UI Professional which is distributed under a commercial license. A comparison of features, use cases and pricing can be found here. The beauty of Kendo UI is that, in addition to the widgets it provides for most common user interface elements, it also provides frameworks and utilities such as MVVM, SPA, DataSource, Templating mechanism, Drawing API, Drag and Drop and other special effects that abstract away the more tedious structural and design aspects of modern web applications. As a developer you have a complete toolset and APIs to build all the layers of a slick and high performance web application.

In this blog series, my intent is to demonstrate some of the capabilities of Kendo UI through concrete examples. The focus is on functionality rather than elaborate user interface wizardry. Today, I will demonstrate the use of Kendo's MVVM, DataSource and Template framework alongside the grid and dropdown list widgets to display a master-detail view of data from a database.

Create HTML Page

Start by opening your favorite code editor and create a stub for your web page as shown below.

 
 

Add Script and CSS References

Kendo UI is a series of JavaScript and CSS files (a common CSS and several theme-based CSS files) and the scripts have a dependency on jQuery. The Kendo CDN is hosted on Amazon CloudFront or you can download your own local copy from the Kendo UI website or NuGet. Kendo also supports AMD (JavaScript Asynchronous Module Definition) API so the scripts can be loaded asynchronously on-demand rather than synchronously during page load. We will discuss these concepts in later parts of the series. For now, add the following CSS and JavaScript references in that order to the <head> section of your html file. Notice that this example utilizes the 'bootstrap' theme.

 

Add HTML Markup to Page

Insert the following HTML markup inside the <body> tag.

 

There are several noteworthy aspects to this markup.

  • Kendo UI supports declarative binding of user interface elements through the use of custom data-* attributes. The data-role attribute is used to specify the Kendo UI widget that the HTML element will be converted into. Other widget properties (e.g. textField, valueField properties of a dropdown list widget or sortable, pageable properties of a grid widget) can also be specified declaratively on the element and they will be used during widget initialization. You can use the data-bind attribute to initiate a dynamic binding of certain widget properties and element attributes to values in a view model. Note that the binding markup is not JavaScript even though it looks like that. So inline JavaScript in attribute binding (e.g. text: somevalueFromVM.toUpperCase()) is not supported. Any processing must occur in the view model.
  • All data source bound Kendo widgets populate the data source during widget initialization (synchronous). This can be prevented by specifying data-auto-bind="false" on the widget. You can then initiate the data source population in response to a specific event. In this example, the 'change' event handler on the dropdown list widget calls the 'filter' method on the zioStatsDS data source. This call internally initiates the 'change' event on data source when data has been retrieved and this in turn triggers the 'binding' event on the grid.
  • The declarative binding and MVVM support provide a clean 'separation of concern' between user interface code (View) and the code that manipulates this view and interacts with the data (View Model). Indeed, the view model can be separated out into its own JavaScript file and loaded asynchronously, thus maintaining a fluid and responsive user interface.
  • Many Kendo widgets, especially those that display data from a data source support a 'templating' mechanism. Templates are a substitute for building HTML strings using JavaScript string concatenation. A Kendo display template is initiated with a <script type="text/x-kendo-template" id="someid"></script> markup. The type attribute of the script can be anything other than the common attributes such as text/javascript. Each data row within a data source is passed to the template to render. Kendo UI Templates use a simple templating syntax called 'hash templates'. With this syntax, the # character is used to mark areas in a template that should be replaced with data when the template is executed. The # character is also used to identify the beginning and end of custom JavaScript code inside the template.
    • To render a value as HTML, use #=  value #
    • To keep HTML encoding while displaying a value, use #: value containing HTML tags #
    • To execute arbitrary JavaScript code, use syntax such as # if (true) { # ... non-script content here ... # } #
    • If the template includes a literal # character, which is NOT part of a binding expression and is not a script code marker, then you must escape that character or you will get an 'Invalid Template' error during compilation. This can happen if a # is used inside a hyperlink URL which is generated during template execution or a CSS color attribute identified by its hex code such as #000000. Literal # in JavaScript strings are escaped with \\\\#, while literal # in external HTML script templates are escaped with \\#. If the template includes a # character which is part of a binding expression of a nested template (e.g. # used for jQuery selector), then this character must also be escaped as \\#. That way, the character will be ignored by the outer template, but will be processed by the nested template.
  • The master-detail display is managed by the 'detailInit' event handler on the master which displays the detail list using the template specified in data-detail-template attribute on the master.

Add Application Script

Insert the following script at the end of the HTML markup inside the <body> tag. Note that you can also create a separate JavaScript file and reference it within the HTML file.

Some important points,

  • The kendo.data.ObservableObject is at the heart of Kendo MVVM framework. It supports change tracking and subscriber notifications. All view model objects inherit from kendo.data.ObservableObject. We can explicitly create an instance of the ObservableObject using var vm = new kendo.data.ObservableObject({}) or though the kendo.observable({}) method which are passed a regular JavaScript object containing properties and methods. The kendo.bind() method converts the JavaScript object into an ObservableObject. However, it will NOT convert a regular JavaScript object into an ObservableObject unless that object was first passed to the observable construct. That's why it is advisable to always create the view model object using the kendo.observable method or new kendo.data.ObservableObject({}).
  • To retrieve the properties of an ObservableObject from inside the view model, use this.get('propertyName') construct. Outside the view model (e.g. other javascript function in the same file) you reference the view model variable instead of this. To access a nested property, you use the this.get('parentPropertyName.childPropertyName') construct. To set a property on an ObservableObject, you use the this.set("gridVisible", true) construct.

Kendo Data Source

The data source object plays a central role in data centric Kendo UI web applications. It is capable of,

  • Using local data (arrays of JavaScript objects) or remote data (web service endpoints).
  • Maintaining structure and type of source data in-memory using the 'schema' property. When the response is NOT a plain array of JavaScript objects, a 'schema' must be defined. In the example above where function getZIOStatsDS(myServiceBaseUrl) returns a Kendo DataSource object, the content type of the response from the remote service is application/json;odata=verbose. The data we wish to process is actually in the 'value' property of the JSON response. Hence we map the data, total and model properties of the data source like so - data: function (data) { return data.value; },total: function (data) { data["odata.count"]; }.
  • Processing source data to provide calculated values (e.g. generate a full_name field by concatenating first_name and last_name fields in the source) using the 'parse' property.
  • Processing different serialization formats - JSON, JSONP, OData or XML - to/from remote endpoints.
  • Maintaining in-memory cache of changes to synchronize with remote endpoints.
  • Performing full CRUD (Create/Read/Update/Delete) operations against remote endpoints and synchronizing changes.
  • Providing ability to read, fetch, query and filter the data.
  • Supporting client and server side filtering, grouping, sorting, paging and aggregating data.
  • Offline storage with automatic remote endpoint sync when browser connection is detected. Offline storage is implemented using localStorage which is similar to sessionStorage.

Sample Data

You can test this using the following sample data. The application script will have to be modified to create the 'masterData' and 'detailData' properties on the view model. These will be used to simulate the JSON stream that would otherwise be received from the OData service.

Accordingly, modify the 'zioStatsDS' property in the view model as shown below

 

Also modify the variable declaration inside 'initHistoricDetails' property in the view model like below

 

Finally, modify the main data source function like so

 

Note that instead of the 'transport' property used to retrieve data from remote endpoints, you use the 'data' property to bind to local data.

Below are a few screenshots of the application in action. You can view the application in action. You can also download the complete source.

blog_candowithkendo_1.png 

blog_candowithkendo_2.png 

Summary

RequireJS and AngularJS integration.

Information and material in our blog posts are provided "as is" with no warranties either expressed or implied. Each post is an individual expression of our Sparkies. Should you identify any such content that is harmful, malicious, sensitive or unnecessary, please contact marketing@sparkhound.com.

Meet Sparkhound

Review our capabilities and services, meet the leadership team, see our valued partnerships, and read about the hardware we've earned.

Learn How We Work

See how our Plan/Build/Run methodology drives real client success, and gain our team's perspectives on timely tech topics.

Engage With Us

Get in touch any of our offices, or checkout our open career positions and consider joining Sparkhound's dynamic team.