Quantcast
Channel: Kendo UI Blogs
Viewing all 181 articles
Browse latest View live

Building The Wisconsin Gameday Mobile Application

$
0
0

Being a web application developer in higher education brings with it numerous challenges, not the least of which is the sheer diversity of projects. Case in point: In January 2012 the Athletic Department here at the University of Wisconsin contacted my group to assist them in development of a new mobile app for fans of the football team. This presented a unique opportunity to us as our experience developing mobile apps was relatively minimal yet this app would have a very high degree of visibility. I won't lie to you - there was some amount of trepidation when we accepted the project without knowing exactly which framework we were going to utilize!

During the requirements gathering process, we quickly decided that we needed to start prototyping a variety of potential solutions utilizing the various app development frameworks made available to us. It was made clear to us from day one that we needed to push out both an iOS and Android version by a quickly-approaching launch date of mid-August. Not to mention, there was a nagging voice in the back of my head that told me we had to somehow leverage our existing skillsets developing applications on the Microsoft stack. So where do we begin?

First Attempt: Native Tools

We knew this had to be a professional-looking and native-feeling app from top to bottom. Clearly the only way to achieve this would be to develop the apps natively. Objective C can't be that bad and Java is incredibly similar to C# right? So we're good to go! Not so much. This was a non-starter from the get-go. While I have nothing but respect for native app developers, there is just no way that we could justify the amount of learning and training time we would have had to provide for a fully native implementation.

Second Attempt: Native Tools (with a .NET twist)

After our brief failed affair with Apple's tools, we decided to look more closely at a couple of frameworks we have heard a lot about: MonoTouch and Mono for Android. If you're not familiar with the Mono project, it is essentially an open source, cross-platform implementation of the .NET framework. MonoTouch is their product for developing iOS apps and Mono for Android is the equivalent for developing Android apps. It's the best of both worlds right? I get to use my favorite language (C#) but still develop a truly native app experience!

And we took off with this idea for our prototype. And we were flying with it. And then we hit a snag. And then another and another. Our prototype implementation was being bogged down by our lack of knowledge of Xcode (yes you still have to use Apple's IDE) and the lack of a presence of the MonoTouch/Mono for Android toolsets on popular sites such as StackOverflow. With all due fairness to the Xamarin team, they have done some amazing things. For us, though, we had too tight of a turnaround time and couldn't afford the risk of investing our time in a platform that was relatively new and just seemed a bit underutilized in the community at the current time.

Third Attempt: HTML5

This is where I'm supposed to act embarrassed, look down at my feet, and apologize for taking the easy way out. You've heard it all before:

  • There is no way to deliver native-like performance with HTML5/JS/CSS3.
  • The current crop of mobile JavaScript frameworks is too slow.
  • Mobile devices are underpowered with regards to JavaScript execution.

You got me there. In fact, we had developed a few small mobile apps with PhoneGap and jQuery Mobile. The end results, while very functional and nice looking, did not feel like true native apps.

Everything changed the day I read about Kendo UI. Here was a new JavaScript framework (based on jQuery AND with a mobile implementation!?) from a company we have trusted for many years as .NET developers. The deal was sealed when I read the word "performance" on the front page of their site more than a few times. (Hello CSS3 transitions!)

Needless to say, our prototype was incredibly successful. We were up and running in no time. Soon we found ourselves:

  • Developing on Windows using our favorite IDE (Visual Studio)
  • Leveraging the years of experience we have with HTML/JS/CSS
  • Playing around with a fun and intuitive new JavaScript framework

Upon completion of our prototype we came up with the following (admittedly-subjective) metrics:

  • Application performance (including view transitions) was at least 90%-95% of what we would expect from a native implementation.
  • Development time was maybe 10% of what we were seeing with the native toolsets.
  • We were spending maybe 2% of our time on learning and training as opposed to 50%+.

Kendo UI it is!

Development

Fast forward a month. We have in our hands a full requirements document and are ready to start development. It's important to remember that when you are developing an HTML5 mobile app, you are actually developing a Single Page App (SPA). This was important for us as something to keep in mind regarding performance, memory-utilization and resource-management. We would have to be smarter about how we write our JavaScript and more careful in its utilization.

Before I go any further, I have been asked specifically about my development environment. It's quite simple really. Since we were developing an iOS app we still needed a Mac (although you could use a service like PhoneGap Build or Telerik's own Icenium to avoid this). After creating the PhoneGap project in XCode (see Burke Holland's post) it was a simple matter of setting up an SFTP share on the Mac which I could connect to with my Windows PC using a tool like ExpanDrive. Make a change in Visual Studio -> Copy Web Site -> Clean and Build in Xcode -> Run in Simulator. Not quite as easy as traditional web development, but surprisingly painless. If I was testing functionality only I could usually get away with using a WebKit-based browser such as Safari or Chrome on Windows (YMMV though).

So what made Kendo UI so helpful for us? I think it's best if I show you a couple of really simple code samples directly from the Wisconsin Gameday app to really demonstrate what Kendo UI can add to your projects.

Twitter Integration - HTML

<div data-role="view" data-layout="contentLayout" id="twitterBadgerFootball" data-show="getBadgerFootballTwitter"><ul id="twitterBadgerFootballListView"></ul></div><script type="text/x-kendo-tmpl" id="twitterTemplate"><div class="tweet"><img class="twitterProfileImage" src="${profile_image_url}" /><strong>${from_user_name}</strong><span class="twitterFrom">@${from_user}</span><p class="twitterText">#=text#</p></div></script>

Twitter Integration - JavaScript

function getBadgerFootballTwitter() {
    var dataSource = new kendo.data.DataSource({
        transport: {
            read: {
                url: "http://search.twitter.com/search.json",
                contentType: "application/json; charset=utf-8",
                type: "GET",
                dataType: "jsonp",
                data: {
                    q: "from:BadgerFootball"
                }
            }
        },
        schema: {
            data: "results",
            total: "results_per_page"
        }
    });

    $("#twitterBadgerFootballListView").kendoMobileListView({
        dataSource: dataSource,
        pageable: true,
        template: kendo.template($("#twitterTemplate").html())
    });
}

 

The preceding code snippets are a simplified example of what it took us to pull in a Twitter feed on the fly. Here you will see an example of Kendo UI's template system, the Kendo DataSource component, and runtime implementation of the Kendo Mobile ListView.

The following is another really simple of example of how we can take an external data source and, using a template and a few lines of JavaScript, turn it into a functioning, scrollable, Kendo Mobile ListView:

UW Badgers Blog - HTML

<div data-role="view" data-layout="contentLayout" id="blog" data-show="showBlog"><ul id="blogHome"></ul></div><script type="text/x-kendo-template" id="blogHomeTemplate"><a href="\#blogDetail?guid=${Guid}">
    ${Title}<br /><span class="normal">
    # var arrDate = PubDate.split(/[-T:]/), myDate = new Date(arrDate[0], arrDate[1]-1, arrDate[2], arrDate[3], arrDate[4], arrDate[5]); #
    ${kendo.toString(myDate, "dddd MMMM d, yyyy")}</span></a></script>

UW Badgers Blog - JavaScript

function showBlog() {
    $("#blogHome").kendoMobileListView({
        dataSource: blogDataSource,
        template: $("#blogHomeTemplate").html(),
        style: "inset"
    }).data("kendoMobileListView");
}

 

We were tasked with utilizing existing data stores that would provide us the content for our blog view, news view, schedule, concessions, etc. We handled all of this by utilizing our existing knowledge of creating WCF Services (but you could just as easily use the ASP.NET Web API). Again, utilizing the Kendo UI template system and Kendo DataSource component time and time again we were able to easily pull in this remote data and format/display it for our users. Incredibly easy to write the code and extremely fast for the client to consume and process.

Making the Leap from iOS to Android

Once we had a functionally-complete app running in iOS, we began our Android implementation. Aside from referencing the Android version of the PhoneGap library and the Android Kendo UI CSS, the extent of our Android conversion was copying our source code to an Eclipse project! (I believe we had a functioning Android version within about an hour.) I don't want to give you the false impression that we were done in an hour (there was plenty of CSS tweaking and minor JS code changes to be worked in) but when it was all said and done we had a completed Android version in probably 10% of the time it took us to develop the iOS version (the same would have been true if we had started with Android of course).

Application Release and Post-Mortem

Once both apps were released on their respective app stores, we started our post-mortem review. What could we have done better or differently? What did we learn that will help us in the future? Two big things stood out for us:

  • When developing a multi-platform app, start with Android. In general, performance is not as solid as iOS so by starting in Android you're setting a minimum baseline of performance.
  • Try to leverage existing JavaScript libraries such as Knockout, Breeze, etc - much work has been done to make your lives as developers easier, take advantage of this!

As an aside, I need to mention that for fun we decided to take one of our old jQuery Mobile projects and re-write it using Kendo UI. Since the two frameworks are relatively similar (syntax-wise), it took us all of 45 minutes to convert a small app (12 views). Amazing!

If you are interested in looking at the free Wisconsin Gameday app itself to see some functional examples of what you can accomplish with Kendo UI, you can download it on Apple's App Store here or on Google Play here.

I hope this helps you as mobile developers if you are considering one of the routes we took. At this point, we are not looking back. The HTML5 stack is the future and Kendo UI is providing us the tools we need to take us there.

About the Author
is a web application developer and project manager from Madison, WI. He works as an IT Consultant for the University of Wisconsin-Madison. Rob only recently jumped on the twitter bandwagon - @rdlauer - be gentle!


Using The Docs To Bend Kendo UI To Your Will

$
0
0

Sometimes you have business requirements that are not (gasp) straightforward. In the real world we don't all have a perfectly normalized Northwind databases and simplistic requirements that fit nicely inside the neat and tidy boundaries of any UI framework. 

When adding new features to Kendo UI we include functionality that people have told us loud and clear is needed. Unfortunately, we cannot possibly include ALL THE THINGS for all the people out of the box. That would be one ENORMOUS JavaScript library! For more information about how new goodness does make it into Kendo UI, see Brandon's roadmap post for a rundown on how those decisions are made. It turns out it DOES NOT involve a “jump to conclusions” mat.

Since not all of what you need to just drop some widgets in and be done with things can possibly be included in Kendo UI, it's likely that you are going to need to do some amount of customization.

Kendo UI was built knowing that this is the reality of software development. Telerik has been making developer tools for over a decade, and we know it's no help to you to assume that we can deliver you a control that will be everything to everyone. We work hard to make sure that Kendo UI is structured in such a way that it won't box you into a corner. It's endlessly customizable and extendable.

Today, we are going to pretend we are doing some real world development here and it's going to require some features from the AutoComplete widget that are not there out of the box. We'll use the Kendo UI Docs Site as our guiding light and examine the beauty and elegance of a framework that has anticipated your need to build in your own features.

Multiple Selection

The AutoComplete supports multiple selection out of the box. It's really easy to configure too. All you have to do is specify the separator value.

AutoComplete With Multiple Selection

var autoComplete = $("#auto").kendoAutoComplete({
    dataSource: {
        transport: {
            read: "Home/GetPeople"
        }
    },
    separator: ", "
}).data("kendoAutoComplete");

 

This will allow you to select an item and then select another item. Each item will be separated by a comma. Now let's apply some "real world" business rules to this because it's just way to simple as it is.

Lets suppose that you need to have multiple selection, but once a user has selected an item it should NOT be present again in the list. In other words, they should only be allowed to choose an option once. Out of the box, the AutoComplete doesn't have a switch or mechanism to do this. It would be nice if there were a allowSameValueMultipleTimes: false, but alas. You have a handful of choices here. You can head over to the forums to see if anyone has posted about this issue. With the recent move of the public forums to StackOverflow where visibility is much higher, there is an excellent chance someone else has had the same requirement and posted their own solution.

We can handle this one though! All we need is some basic knowledge about the Kendo UI DataSource and our JavaScript skills coupled with a dash of creativity. This is what makes programming fun.

It's All About The Filters

The first thing that we need to know here is how the DataSource is working. Right now, it's making an initial call to the Home/GetPeople method on the server. It's retrieving ALL of the data and then building the AutoComplete list and doing client-side filtering of the data depending on what is keyed into the AutoComplete. Assuming that we have control over the server method, we can do our sorting there by turning on serverSorting on the DataSource.

Turn On Server Side Sorting

var autoComplete = $("#auto").kendoAutoComplete({
    dataSource: {
        transport: {
            read: "Home/GetPeople"
        },
        serverSorting: true
    },
    separator: ", "
}).data("kendoAutoComplete");

 

Now the DataSource will not try and filter the AutoComplete, but will make calls to the server to have it do the sorting. The problem is that it's only ever going to send one filter to the server, and that's the one that contains the value that we keyed in. We need to send some additional filters depending on what's already in the AutoComplete. To do this, we are going to need to intercept the request that the DataSource is going to send and add some filters to the filters array. The parameterMap method on the DataSource is there just for this specific scenario. What we need to do is get any items that have already been selected in the AutoComplete and add them to the filters parameter array in the request that's about to be sent. This is going to require some creative JavaScript, but nothing complex. This is the basic algorithm for getting these values:

  • Get the text of the AutoComplete.  According to the docs, we can do this by calling its by calling its value method
  • Use the JavaScript split method on the string value to split it into an array of substrings based on the splitter that we define. In our case, it's the separator for the AutoComplete
  • Pop off the last value because it's either an empty string or the value that we are currently typing in, not one we have already selected
  • Add the remaining items from the split to the filters array on the DataSource

Before we do that, we need to know what the structure of the filters object and is and how to access it. First let's have a look at the documentation for the parameterMap method. You can see from the docs that there are two parameters passed to the parameterMap function: data and type. It says the the data parameter Contains key/value pairs that represent the request.. That's what we need. We need to modify the filter parameters on that object. In order to see where to put them and what format to put them in, we need to know how the filter object is structured. Lets head back to the API docs for the DataSource and filter it by filter. If you scroll down, you will see the serverFiltering documentation and the structure of the filter object in the request.

filter_object

 

Another way to figure this out is to make a request with the AutoComplete after toggling on serverFiltering and you will be able to see it in the developer tools.

 

filters_request

You can see the parameters are being passed into the query string, but they are hard to read because they are url encoded (pink). However the Chrome Developer tools have them parsed out below (blue) for our convenience.

Now we know that the object we need off of the data parameter is filter which has an array of filters. Each filter has value, operator, field and ignoreCase properties. Now we know exactly what kind of object we need to pass in. But what operator do we need? We know it needs to be a "not equals" operator. To know for sure, let's check the docs again. The same serverFiltering topic on the API docs will tell us what our choices are.

operator

Now we have enough info to implement the algorithm to add additional filters.

Add Selected Items To Filter

var autoComplete = $("#auto").kendoAutoComplete({
    dataSource: {
        transport: {
            read: "Home/GetPeople",
            parameterMap: function(data, type) {
                // split the values into an array
                values = autoComplete.value().split(autoComplete.options.separator);

                // pop off the last one as its not a selected value
                values.pop();

                // loop through the selected values and add them to
                // the filter criteria to be sent to the server
                $.each(values, function(index, item) {
                  data.filter.filters.push({ field: "Name", ignoreCase: true, operator: "neq", value: item });
                });
            }
        },
        serverSorting: true
    },
    separator: ", "
}).data("kendoAutoComplete");

 

You can see this in action here working with the Netflix OData Source. Notice that you cannot select the same movie twice from the AutoComplete.

Thicken The Plot

But this is still too easy. Netflix has a nice OData API that lets me send parameters and filter/page without much issue. Being able to implement this server side is just too easy. Even more real world requirements might dictate that our list of data to be shown in the AutoComplete is a local set of data. An array that is populated from a model on the server during page load or perhaps just a static list of items.

Our current implementation won't work anymore. The reason why is that when the DataSource is bound to local data, it does all the operations locally and only reads one time. meaning serverFiltering has no effect and parameterMap never gets called. That's a problem because we need the DataSource to read every time in order to apply our additional filters to it. Maybe we can listen for a change event in the AutoComplete. Let's check the docs to see what events are there. I'm going to head over to the API docs and look under the AutoComplete. If I hover over the Events link at the top I can see a snapshot of what events are available.

autocomplete_events

Well, the only event I see there that may help me is the change event. If I click on events and go down to the change event, it says:

Fires when the value has been changed.

Is that what I need? Let me attach a change event listener to a simple AutoComplete. I'll just throw up an alert every time it changes. I just want to see what constitutes an actual change.

Ok, so it fires when I actually select something from the dropdown. That's not going to help me. By then it's already too late. What I need to do is read from the local DataSource like I do from a remote DataSource. Then I could filter the results out. Let's check the read method on the DataSource transport and see if there is some way we can intercept the reading of local data. According to the docs, I can specify a function for the read settings.

custom_transport

This is what we need! This way we can have the DataSource act like it's calling a remote source, but we'll override the read method to specify a local source instead and return that as the source of the data. The official name for this is a Custom Transport.

But how do we let Kendo UI know what data to use? If you look at the image above you can see that they are passing the results of their AJAX request to options.success(). That means that we could pass a local set of data to that same method. We just need to filter it first and then pass it in. What we actually need is two DataSources. The first one will just hold the local data and the second will have a function defined as it's read. The reason why we use a second DataSource is that it will do things like filter local data for us. How do I know this? I checked the DataSource API docs and it has a filter method.

datasource_filter

It says *Gets current filter or filters the data *. That's what we want to do. Filter the data. If you scroll down a bit more you see some examples of filtering where they are passing an array of filter objects which look EXACTLY like the ones we are composing.

filtering_examples

The new logic looks like this:

  • Create a DataSource bound to a local array
  • Set the read method on the transport of the AutoComplete DataSource to a function
  • Inside the function
    • Call our parameterMap method to build the filters
    • Read the local data source
    • call the filter method on the local datasource passing in the filters
    • return the results to the options.success method

Custom Read Function

var people = [ "Korchev", "Brandon", "Todd", "Derick", "Rob", "Ryan", "Burke" ];

// create a datasource bound to the local data       
var peopleDS = new kendo.data.DataSource({ data: people });

var autoComplete = $("#auto").kendoAutoComplete({
    minLength: 3,
    separator: ", ",
    dataSource: {
        transport: {
            read: function(options) {

                // call the paramterMap passing in the key/value options
                this.parameterMap(options.data);

                // read from the local datasource
                peopleDS.read();

                // filter the local datasource
                peopleDS.filter({ logic: "and", filters: options.data.filter.filters });

                // pass the result of the local data source to the options
                // success method
                options.success(peopleDS.view());
            },
            parameterMap: function (data) {

                // split the values into an array
                values = autoComplete.value().split(autoComplete.options.separator);

                // pop off the last one as its not a selected value
                values.pop();

                // loop through the selected values and add them to
                // the filter criteria to be sent to the server
                $.each(values, function(index, item) {
                    data.filter.filters.push({ field: "", ignoreCase: true, operator: "neq", value: item });
                });

                return data;
            }
        },
        serverFiltering: true
    }
}).data("kendoAutoComplete");

And with that we have added the missing functionality to our widget without any hacking of Kendo UI code! All we did was use the docs to determine what API's, options and methods were available and then we added what we needed. A simple and elegant solution if I do say so.

Use The Docs Luke

When we first started just over a year ago, we had a lot of material that we needed to add to the documentation. Since then, we have chocked it FULL of useful examples and mapped out virtually every nook and cranny. Chances are that if you are looking for something, it's there. You just might not have found it yet. Once you get used to using the documentation in conjunction with your developer tools, you will find yourself moving through the framework with the greatest of ease. What’s more is that you will see a certain cadence in Kendo UI and eventually you will be able to blindly guess at parameters and know what they will be because consistency is the strong driving force of the API design behind Kendo UI.

Remember that if you do find a spot where you think the docs could be better, submit an issue over at our GitHub repo.  This way we can continue to refine the docs to better suit you based on your feedback.

Download Kendo UI today and tame those wild business rules with the Kendo UI Docs and a little bit of that developer creativity that got you into this line of work to begin with.

About the Author
is a web developer living in Nashville, TN. He enjoys working with and meeting developers who are building mobile apps with jQuery / HTML5 and loves to hack on social API's. Burke works for Telerik as a Developer Evangelist focusing on Kendo UI. Burke is @burkeholland on Twitter.

Knockout.js and Kendo UI - a Potent Duo

$
0
0

Knockout.js is a popular JavaScript framework for building dynamic user interfaces using the Model-View-ViewModel (MVVM) pattern. Knockout allows for easy one or two-way synchronization between your data and the markup. However, Knockout does not include any widgets or components for creating the user interface, which is a major strength of Kendo UI. The open source library Knockout-Kendo provides the integration needed to make these tools work together in a natural way.

Why use Knockout with Kendo UI?

Kendo UI does provide its own MVVM functionality that is tightly integrated with the Kendo UI widgets. If you are starting a project from scratch and plan to make heavy use of Kendo UI, then you might want to consider this option. However, you may already have experience with Knockout, want to add rich widgets to an existing Knockout-based user interface, or you might just prefer Knockout's syntax and functionality. In that case, Knockout and Kendo UI can be a powerful combination.

When using Knockout, all interactions between the DOM and your data are intended to happen in bindings. Knockout provides an extensibility point that allows you to extend the default bindings with your own custom bindings. Knockout-Kendo works by creating 23 custom bindings that represent all of the Web and DataViz widgets. Each custom binding conveniently handles the plumbing for these tasks:

  1. Instantiating the widget with any provided configuration options
  2. Subscribing to applicable events to update the view model data when users interact with the widget
  3. Calling methods on the widget in reaction to changes in observable view model data
  4. Destroying the widget if the element is removed from the page by Knockout

Using Knockout-Kendo

Getting started with Knockout-Kendo is easy. The library is hosted here on github and includes a documentation site that describes how to use each binding.

Installation

The Knockout-Kendo library will register bindings for any widgets that are available when it is loaded. This means that you will want to reference both Knockout and the Kendo UI scripts (along with jQuery) prior to Knockout-Kendo.

<script src="js/jquery.min.js"></script>
<script src="js/kendo.web.min.js"></script>
<script src="js/knockout-2.2.0.js"></script>
<script src="js/knockout-kendo.min.js"></script>

 

Knockout-Kendo is also AMD-aware, so it can be used with a dependency management library like require.js. In that scenario, the module depends on "knockout", "jquery", and "kendoui" modules. Depending on your setup, the "jquery" and "kendoui" modules may need to be added as part of the shim configuration.

Binding Basics

Each widget is represented with a custom binding that uses the same name as the widget. For example, the slider widget is available as a custom binding called kendoSlider. Each binding accepts an object containing any of the options that the widget supports:

<input data-bind="kendoSlider: { value: weight, enabled: enabled, min: 0, max: 100 }" />

 

The binding also supports a default option for the most common use case, which helps to keep the markup clean. For example, the slider widget can be bound directly against its value option like:

<input data-bind="kendoSlider: rank" />

 

Live options

While the bindings will accept any option that the particular widget supports, some of the options are designated as "live". When bound against an observable, these options will update the widget and/or be updated by the user interacting with the widget. The "live" options are based on the available APIs and events that the widget supports. The Knockout-Kendo documentation contains a section for each binding describing the "live" options for that widget.

For example, the slider has an enabled live option that you can bind against an observable boolean. As the value is toggled, the binding takes care of calling the widget's enable method to properly update the widget's state. There is generally no need to have a reference to the widget or call methods directly on the widget from your view model. This allows you to focus on the business logic and getting your data into an appropriate state to be bound against.

Here is a sample demonstrating the use of the kendoSlider binding:

Global options

Passing complex objects to a binding in markup can get verbose. One option to help keep your markup clean is to bind it against an object in your view model that represents the binding's options. However, many times you will want to reuse the same configuration each time that you use a binding. To support this scenario and to keep the markup simple, each binding supports global configuration options that can be defined in JavaScript. For example, you may want to set a default minimum and maximum for the slider widget:

ko.bindinghandlers.kendoSlider.options = {
    min: 0,
    max: 100
};

 

Now in cases where you do not need further customization of the options, you can simply bind against the value of the slider. The bindings will take the global options as defaults and then apply any configuration passed in through the binding string as overrides.

Advanced usage

To support such a large number of bindings, the Knockout-Kendo library uses a factory approach where bindings are built by passing in a configuration object that describes the widget's relevant events, methods, and options. Since the structure of each widget is similar, this approach makes it easy to generate additional bindings for new widgets and keeps the core logic centralized.

Generating your own bindings

You can also use the binding factory to generate bindings for your own custom Kendo UI widgets. For example, suppose that you create a custom "click-to-edit" widget, where you you display text as a link and when a user click's on it, then it becomes an input field for editing. Let's say that this widget exposes value and allowEditing methods to programmatically control the behavior of the widget. Additionally, the widget might trigger edit and view events when it changes state and a change event when the value changes.

To create a custom Knockout binding against the widget, we can call ko.kendo.bindingFactory.createBinding like:

ko.kendo.bindingFactory.createBinding({
    name: "kendoClickToEdit",
    defaultOption: "value",
    //events to bind against. update the view model based on interactions with the widget
    events: {
      change: "value",
      edit: {
        writeTo: "editable",
        value: true
      },
      view: {
        writeTo: "editable",
        value: false
      }
    },
    //observables to watch. react to changes by calling methods on the widget
    watch: {
      editable: "allowEditing",
      value: "value"
    }
});

 

The options passed to the binding factory have some flexibility in how they can make updates. In the above sample, when the change event is triggered, the binding will call the value method to retrieve the current value and update the observable passed as the value option. However, for the edit and view events, we indicate that we want to write to the observable passed in as editable and write a fixed value (true or false). A good way to understand the available options is to review how the bindings for each widget are prepared in the unminified source code.

Here is a sample demonstrating a custom Kendo UI widget and a generated Knockout binding for it:

Summary

Knockout.js and Kendo UI can be a terrific combination for developing slick and dynamic user interfaces. The Knockout-Kendo library is open source (MIT license) and provides everything that you need to use Kendo UI widgets through Knockout bindings. I am always happy to hear feedback, help with issues, or accept contributions related to the Knockout-Kendo bindings. Visit the project here or browse the documentation to get started today.

About the Author
has been working with web technologies for the better part of 13+ years and lives just outside Madison, Wisconsin with his wife and two daughters. In his spare time, he loves to participate in open source development. He is a member of the Knockout.js core team, has written a number of Knockout plugins, and blogs about his experiences at http://knockmeout.net.

Merry Christmas And Happy Holidays From Kendo UI

$
0
0

You know they don't call it the "most wonderful time of the year" for nothing! We hope that this week brings happiness and peace to you and your families. May you get a break from the blur that life can become balancing a job, your family, and all your other commitments.

Here at Kendo UI, most of us are on vacation this week with our families. Those of us that have very small children (Brandon, John, Todd, Rosen, ect..) will be watching a lot of Teletubbies, giving piggy back rides, playing with Duplo Bocks and whatever you people with tiny tots do to pass the time. Some of us that have older children (yours truly) will be playing a lot of PS3, watching episodes of Mythbusters we have already seen a thousand times and *cringe* old episodes of McGyver.  If you really want to see some bad TV, watch some of those old episodes on Netflix.  I have 3 boys and they absolutely love it.  The guys/girls in Sofia will of course also be up to their own holiday revelries, both in Bulgaria, and around Europe with family and friends.

I wanted to leave you with something fun for the holiday week, should you be reading your feeds or checking up on content this week. Even if you find yourself back at your desk or crunching through a deadline. Hopefully this post will add a bit of glimmer to your week and leave you lighthearted. This is a list of fun and interesting things that I have come across this past week. Some of these are HTML5/Web related and some are just funny.  I typically don’t do list posts as there are so many out there already, but hey – its Christmas!

So here's us wishing you a very Merry Christmas and a Happy HTML5 Holiday season.

Christmas ASCII Art - Everyone loves ASCII art and I couldn't pass this one up. It's on GEOCITIES!!!

Unicode Snowman For You - Totally pointless, but it's an Unicode snowman. And it's for you. Sadly, it is not a valid JavaScript variable, but see Mathias Bynens post for all sorts of ridiculous Unicode characters that are legit. I'm not saying you should use them. I'm just saying.  via Digital Bush

Node XMAS Module For all you Noders out there,

npm xmas

You're welcome.  via Brandon Satrom

 

HTML5 Canvas Snow In 3d - You are probably going to need a WebGL capable browser for that one.

NORAD Tracks Santa With HTML5 - Every year NORAD tracks Santa and this year they are using more HTML5 to do it, including crazy stuff like WebGL, Web Workers, Typed Arrays and CORS. Mozilla has a nice write up here on what they are using and how they are doing it.

Nice Snow Creations - Some pretty neat snow creations posted by the folks over at thinkgeek.

Git Cheatsheet A nice interactive cheatsheet of git commands.  It has nothing to do with the holidays, I know.  I’m trying here.

Gagnam Style CSS - Because, lets face it. You CANNOT get enough Gangnam style.  via John Bristowe

Quick JavaScript Quiz - This was a quick quiz posted by Ryan Florence wherein you have to implement the functions to pass the tests. Give it a go and see if you can implement all 3. I gave up on the first one but implemented the second two. See if you can do better!  It's a great way to test your skills. You know: nun chuck skills, bow hunting skills, computer hacking skills...

Notch Tries Out JsFiddle - If you are ready to have your mind blown, check out what the creator of Minecraft (Notch) did with some creative JavaScript in JsFiddle.  via Korchev

Offscreen Mag's Web Workers Christmas Wishlist - If you're looking for a creative way to spend all that Christmas money, check out their list of really unique and creative stuff.  There are some excellent items here that I’m betting you haven’t seen before.  via Vasil Yordanov

And Lastly, But Not Leastly

Handiemail - If you want to send thank you notes with that "personal" touch but lack the motivation to do handwritten notes, Handiemail will hand write your message for you for the very low price of $10. There's no better way to say "I care, but I'm incredibly lazy".  via Todd Anglin

See You All Next Year!

2013 is going to be an amazing year for Kendo UI and HTML5. We have some really exciting things on our roadmap, and a few that are still under wraps! We look forward to bringing you all the goodness that the web has to offer in 2013 while continuing on our mission to be the most complete HTML5 framework for web, mobile and wherever else web technologies will take us!

About the Author
is a web developer living in Nashville, TN. He enjoys working with and meeting developers who are building mobile apps with jQuery / HTML5 and loves to hack on social API's. Burke works for Telerik as a Developer Evangelist focusing on Kendo UI. Burke is @burkeholland on Twitter.

[Screencast] Debugging Kendo UI Applications

$
0
0

JavaScript is a beautifully simple and elegant language.  It puts virtually no restrictions on what you can do; and since it’s a dynamic language, you can modify it on the fly and debug it right in the browser.  However, it’s great forgiving nature can also be a source of maddening frustration.  JavaScript will let you call methods and properties that don’t exist, create malformed objects and has zero protection against silly mistakes.  If you are going to be productive with Kendo UI, it’s essential that you become very comfortable with your browser’s developer tools.  These tools will be a more essential part of your workflow than even your IDE.

In this screencast, I make several common errors and use the Chrome Developer Tools to squash all of the bugs while digging myself out of a completely busted implementation of a Kendo UI Grid.  Make sure you check out the additional resources below the video for more information on the Chrome Developer Tools.

Additional Resources

 

About the Author
is a web developer living in Nashville, TN. He enjoys working with and meeting developers who are building mobile apps with jQuery / HTML5 and loves to hack on social API's. Burke works for Telerik as a Developer Evangelist focusing on Kendo UI. Burke is @burkeholland on Twitter.

Common Errors In Kendo UI Applications

$
0
0

As I was putting together the screencast last week for debugging with Kendo UI, I was thinking about all of the common mistakes that I routinely make. Some of them are simple silly errors that come from not reading the documentation, or simply writing bad code. The tricky thing about HTML5 development is that so much of it occurs in the browser, which is terribly forgiving and incredibly damning all at the same time.

The browser will do some degree of auto-correction for you. Notice that you have never been to a page that displayed the message "Unable To Build This Page". The browser is going to display the page as best it can no matter what happens. Even if you do something completely illegal like trying to nest an anchor inside of an anchor, the browser will try and display this for you. What you have probably seen even more often is that if you forget to close a tag, the browser decides where to close it for you. This can give you some pretty unexpected results.

In terms of JavaScript, there is never anything to tell you that you are about to call a method that doesn't exist, or you are trying to use a property that is not on an object. In fact, if you simply set a property that doesn't exist on an object, it will be added to that object.

Setting A Property That Doesn't Exist

// user object has a first and last name
var user = {
  firstName: "Walter",
  lastName : "White"
};

// set the name property which does NOT exist
user.name = "Walter White";

// it exists now!
console.log(user.name);

 

Another thing that "some" browsers will do is hoist your variable into the global namespace if you try to use it, but it isn't defined.

Using A Variable That Doesn't Exist

// user variable doesn't exist
user = "Jessie";
console.log(window.user);

 

Not that not all browsers will handle this. IE 8 will just tell you that the object "doesn't support this property or method", which is not at all what is happening.

So given the massive amount of freedom that you have, you need to be very attentive to details. Get used to checking your syntax visually as your IDE probably isn't going to be much help there when it comes to JavaScript.

You have some tools that you can use as well to help you check your code to make sure it's well formed. Your primary tool is of course your browser's developer tools. That should be the first place that you look when you are not getting the results that you coded for. Another tool that you can use to check your syntax is JSHint.

JSHint

JSHint is a fork of the JSLint utility released by Douglas Crockford. The reason why so many people prefer JSHint over JSLint, is that JSLint will complain loudly about improper spacing and other things that are not errors, but really just style. However, there is firm support from the community that we should all format our JavaScript the same way. For instance, see Rick Walron's Idiomatic JavaScript.

JSHint will tell you if your JavaScript is malformed. If you forget a semicolon, a comma or try to use a variable that doesn't exist. All of the things that might cause your code to straight up break in the browser. You should always "lint" your code before you run it as a double check that you didn't make any errors. If you don't, you will be popping back and forth between your browser and your IDE a lot as you discover all of the small syntax errors you made when the browser complains about it.

To use JSHint, you have several options. Of course, you really want to use it in your IDE. You can add it as a package in Sublime Text 2. There are several, but the "SublimeLinter" appears to be the most feature rich. If you are using Visual Studio, you can add the Web Essentials, which includes JSHint. In both of these, each time you save a JavaScript file it will be linted with JSHint and any errors displayed. The errors should be fixed. Warnings are more of a "heads up" as they may or may not represent a real issue.

Common Kendo UI Mistakes

Now that we have some ground work for doing "idiot checks" on our code, lets look at some of the common mistakes that can be made in Kendo UI so that you can know what to look out for. Some of this material comes from the Troubleshooting Guide in the documentation.

jQuery Is Not Defined

You will see this error if you include your Kendo UI Scripts before you include jQuery. Make sure that jQuery is included before Kendo UI as the browser loads these scripts synchronously and Kendo UI depends on jQuery being first.

[object Object] Has No Method kendoGrid

You may also see it as...

  • $("#grid").kendoGrid is not a function (FireFox)
  • Object doesn't support the property of method "kendoGrid" (IE 9+)
  • Object doesn't support this property or method (IE 8-)

This can occur for a variety of reasons:

  1. You have not included Kendo UI at all
  2. You are referencing the wrong path to Kendo in your page
  3. You include jQuery both before and after Kendo UI which wipes out Kendo

Things Don't Look Quite Right

If you don't include the proper CSS files and assets, your widgets may not look right. What you need depends on what type of application you are building:

Web And DataViz

  1. kendo.common.css
  2. a kendo css theme file (default.css or other theme)
  3. The textures and images folders which contain assets for the stylesheets

Mobile

  1. Either kendo.mobile.all.css or the css file of the platform you are targeting (i.e. kendo.android.css)
  2. The textures and images folders which contain assets for the stylesheets

If you have specified the path incorrectly for these stylesheets, they won't be loaded and your application won't look correct. Always make sure to use your browser developer tools to specify that all of your stylesheets and scripts have been loaded. You can do this by inspecting the Network tab in your developer tools and making sure that you are not getting a 404 error for any of the required stylesheets or JavaScripts.

Casing Errors

JavaScript is a case sensitive language. This means that "dataSource" is not the same as "datasource". That's such a innocuous error that it often slips by unnoticed. It's one that you are going to see the effects of right away though because you won't have any data in your widget.

Casing Errors

// this code doesn't work because the grid doesn't contain a datasource property
$("#dropdown").kendoDropDownList({
    datasource: {
        data: ["Gus", "Hector", "Tuco"]
    }
});

// this is the correct version. 
// notice how hard it is to catch the difference unless you know to look for it
$("#dropdown").kendoDropDownList({
    dataSource: {
        data: ["Gus", "Hector", "Tuco"]
    }
});

 

That's one that I made in the screencast yesterday. Also, data-role="listview" is not the same as data-role=”listView”. The second one will give you nothing, while the first creates a Kendo UI Mobile ListView. In the case of roles, all of the Kendo UI role attributes are lower case.

Declarative Initialization Errors

This is one I see with some frequency. Once you get used to it, it doesn't really show up anymore. When you are using Kendo UI with declarative initialization, you will find yourself needing to translate JavaScript properties into HTM5 Data Attributes. For instance, you may have a DropDown List and you want to set the dataTextField. Your first inclination on how to do this might be to code data-textField="name", but this is wrong. The correct way to do it is to separate the words with a dash delimiter. The correct form is data-text-field.

<div data-role="dropdownlist" data-source="MYAPP.cities" data-text-field="name" data-value-field="value"></div>

<script>

    $(function () {

        window.MYAPP = {};

        window.MYAPP.cities = new kendo.data.DataSource({
            data: [{ id:1, name:"Jaurez" }, { id:2, name: "Albuquerque" }] 
        });

        kendo.bind(document.body);

    });

</script>

 

Data Attributes by definition are not cased. This is true even in jQuery. For instance, if you add a mixed case HTML5 data attribute to your tag and you try to read it with jQuery as mixed case, you will get nothing. You have to read it as lower case.

//this returns nothing because data attributes are lowercase
var result = $("#skyler").data("carWash");
$("#log").html(result);

// this returns the value of data-myField
var result = $("#skyler").data("carwash");
$("#log").html(result);

 

Beware The Cross Domain Barrier

If you weren't already aware, you cannot use AJAX to call for data that resides on another domain or port. This means that if your application is running at app.mycompany.com, you can call a service located at service.mycompany.com, but you CANNOT call a service that resides at service.myservices.com. MyCompany and MyServices are different domains. You can see blocked requests in the browser developer tools.

cross-domain

The browser will block your request for JSON or XML. If you have to call services on different a different domain, you have a few options.

  1. JSONP

JSONP is not the same as JSON. It's a JSON string wrapped in a random JavaScript callback that fools the browser into thinking that you made a request for some JavaScript, not a JSON file. The server that is returning the JSON must support JSONP in order to use it as its up to the server to wrap it up for you. Kendo UI handles JSONP just fine and it's a great alternative if you have control over returning the data as JSONP.

  1. CORS

Short for Cross-Origin Resource Sharing, this new standard is not supported in all browsers and even has quirks in some modern browsers. This is the concept of specifying a cross domain policy which allows communication with a specific external server when that server is marked as safe.

Use The Correct HTML Tags When Initializing Widgets

For the most part, this isn't a problem. However, when you have input widgets - like the Numeric TextBox and AutoComplete, you need to use an input tag to initialize the widget. If you use some other tag, you will see the widget but you won't be able to type in it because it's not an input element.

Malformed HTML

You need to know that the HTML that you are writing is indeed valid. Many IDE's will do this for you. For instance, you cannot nest anchor in an anchor as I mentioned earlier. You cannot nest an input inside of an input and so on.

When in doubt, run your HTML through the W3C markup validation service. You can either upload your file, paste the html in directly, or just point the validator at a page if the page is publicly available.

Having malformed HTML will cause you a world of problems, not the least of which is going to be sporadic functionality across browsers.  Kendo UI requires that your HTML be valid.

Markup And Mobile

When it comes to Kendo UI Mobile, having your HTML structured correctly is of the utmost importance. Even if it's valid, it has to be formed right as per the documentation. Kendo UI Mobile requires a valid mobile application to be correctly defined with HTML. For instance, you cannot use a NavBar widget without putting it in a header. You cannot use a TabStrip without putting it in a footer. If you try to do either of those two things, nothing will happen. If your mobile application is not rendering like you think it should, it's highly likely that your HTML is simply not correct. Double check the documentation for Kendo UI Mobile to make sure that your code matches what is specified in the docs and examples.

Use Your Developer Tools

The biggest favor you can do yourself when building HTML5 applications is to get up close and personal with your browser's developer tools. I'm even going to go so far as to suggest that you use either Chrome or FireFox if you can as the browser tools there are really a cut above everything else. These tools will be the difference between blind guessing and intelligent problem solving.

When you do become comfortable debugging inside a browser, you will see with a tremendous amount of clarity that only a runtime environment like the web can provide. You will iterate faster, code cleaner and learn quickly from your mistakes.

About the Author
is a web developer living in Nashville, TN. He enjoys working with and meeting developers who are building mobile apps with jQuery / HTML5 and loves to hack on social API's. Burke works for Telerik as a Developer Evangelist focusing on Kendo UI. Burke is @burkeholland on Twitter.

HTML5 adoption survey 2013 – your opinion matters

$
0
0

Building on the success and warm reception of the HTML5 adoption survey we did last year, the Kendo UI team is now ready for the second edition. We’d love to get your opinions and feedback about the software industry at large as well as the web and mobile technologies that impact you on a daily basis. In the last survey, over 4000 software developers told us about W3C/WHATWG standards split preferences, and their overall attitude towards HTML5, helping separate hype from reality. This time around we have a new batch of equally important questions and topics that we can’t wait to share and discuss with everyone.  

                                Take the survey NOW!

Please take a few minutes from hacking away at the next great app and participate in the Kendo UI HTML5 Industry Survey 2013. The survey will close on Friday, February 18 at midnight EST. We will share the results with you and the rest of the software developer community in March-April 2013 timeframe.

Your opinion matters!! Thanks in advance for completing the survey


About the Author
is a Marketing Director at Telerik and is based in Toronto, Canada. A geek at heart, Sasha works with HTML and JavaScript developers worldwide and loves learning from passionate people. You can follow Sasha on Twitter @k_sasha.


Using Kendo UI templates with Backbone

$
0
0

Backbone and Kendo make a beautiful combination as we've already seen. But this dynamic duo doesn't stop with the use of the Kendo control suite. Backbone's flexibility and defining characteristic of open and unopinionated structure allow it to be combined with more of Kenod's capabilities than just the controls. For example, we can use Kendo's templates with Backbone views very easily. Before I dive in to that code, though, I'll take a moment to review Backbone's basic view rendering.

Rendering A Backbone.View

Most Backbone.View objects have a render method that is used to generate the HTML content for the view. The actual mechanics of generating that HTML, though, is left up to the developer and team that is writing the application. Frameworks like MarionetteJS, ChaplinJS and others, abstract this detail away by providing defaults that can be overridden easily. But when writing a raw Backbone.View, the choice is left for you to make.

You could easily hard code a string with HTML tags in it:

var MyView = Backbone.View.extend({ 

  render: function(){
    var content = "<h2>Hello there!</h2>";
this.$el.html(content); } });

Or you can use a template engine such as Underscore.js' built in template function, Handlebars, Mustache, Jade, or any of a dozen other JavaScript template engines, including Kendo UI's.

The majority of these template engines have a two-step process for rendering the desired content:

  1. Compile the raw template string in to a JavaScript function
  2. Call the function and apply some data to the compiled template

When working with Backbone, there are a few additional steps that need to be injected such as pulling the raw template content from the DOM and building a JavaScript object literal with the data that the compiled template needs. You also need to populate the view with the resulting HTML string when the template has been run.

The end result often looks like this (using Underscore.js - a prerequisite of Backbone.js and a common choice for rendering Backbone views):

<script type="text/html" id="about-me-template"><h2>About <%= name %></h2><ul><li>Employer: <%= employer %></li><li>Title: <%= title %></li><li>Job Description: <%= description %></li></ul></script>

var MyView = Backbone.View.extend({

  render: function(){

    // step 1: grab the template from the DOM
    var templateHtml = $("#about-me-template").html();

    // step 2: compile the template
    var compiled = _.template(templateHtml);

    // step 3: build an object literal with the data
    // that we want to display in the template
    var data = {
      name: "Derick Bailey",
      employer: "Telerik",
      title: "Developer Advocate for Kendo UI"
    };

    // step 4: apply the data to the template to
    // produce a raw string that contains the HTML
    var content = compiled(data);

    // step 5: populate the view with the result
    this.$el.html(content);
  }
});

Rendering A Backbone.View With A Kendo UI Template

Having established a baseline for rendering a Backbone.View, here is what it takes to convert the above example in to a Kendo UI template:

<script type="text/html" id="about-me-template"><h2>About #= name #</h2><ul><li>Employer: #= employer #</li><li>Title: #= title #</li></ul></script>  

var MyView = Backbone.View.extend({

  render: function(){

    // step 1: grab the template from the DOM
    var templateHtml = $("#about-me-template").html();

    // step 2: compile the template
    var compiled = kendo.template(templateHtml);

    // step 3: build an object literal with the data
    // that we want to display in the template
    var data = {
      name: "Derick Bailey",
      employer: "Telerik",
      title: "Developer Advocate for Kendo UI"
    };

    // step 4: apply the data to the template to
    // produce a raw string that contains the HTML
    var content = compiled(data);

    // step 5: populate the view with the result
    this.$el.html(content);
  }
});

The result can be found in this JSFiddle.

Note that there isn't much of a difference between the underscore and Kendo UI version. In the template, underscore uses <%= ... %> blocks to denote data while Kendo uses #= ... # blocks. Then in the view itself, we only needed to change one line in the render method to say kendo.template instead of _.template. Other than that, the view and template are the same between the two versions of this example.

More Than Simple Templates

As you can see, Backbone's flexibility allows it to be combined with Kendo's capabilities easily. And Kendo offers more than just the simple templates that I've shown above. It has many more options and capabilities that can be found in the documentation and demos, and provides additional functionality such as input validation, globalization and more that can be added in to a Backbone application.

About the Author
is a developer, trainer, speaker and Developer Advocate for Kendo UI. He's been slinging code since the late 80’s and doing it professionally since the mid 90's. These days, Derick spends his time primarily writing javascript with back-end languages of all types, including Ruby, NodeJS, .NET and more. Derick blogs at DerickBailey.LosTechies.com, produces screencasts at WatchMeCode.net, tweets as @derickbailey and provides support and assistance for JavaScript, BackboneJS, MarionetteJS and much more around the web.


Chrome apps accelerated events

$
0
0

Do you know what's better than building a Windows Application? Building a single application that not only runs on Windows, but also runs on Mac. And Linux. AND Chrome OS.

Chrome Packaged Apps run anywhere that Chrome runs. They give you complete access to the device's native API's through JavaScript. Need to read the file system? No problem. Need to access a USB device? There's an API for that. Deploy it to the Chrome Web Store and get a single delivery and update mechanism for your application. This level of reach for desktop applications is completely unprecedented.

This is why we have been working with Google since the early days of Packaged Apps to make sure that Kendo UI works beautifully inside of Chrome Packaged Apps. We didn't just test it, we built a full on application using Kendo UI that is now being shipped by default on Chrome OS devices. As one of the developers of the application, trust me when I say that this is extremely compelling stuff.

Interested yet?

Upcoming Hackathons

If you are near the Boston or New York area, you have the unique opportunity to hang out with the Google Developer Relations team and get in-depth and free training on building Chrome Packaged Apps.

Google will be doing a series of Chrome Apps Accelerated events on the 23rd of January from 9AM - 5PM where they will be working hands on with developers and exploring the Chrome JavaScript API's in depth.

Step 1. Sign Up

Click here to sign up for the event and reserve your spot.

Step 2. Go Prepared

We have a ton of resources to get you started with Kendo UI And Chrome Packaged Apps.

Make sure you visit our Chrome Packaged Apps resources page.

Additionally, you can check out Google's Paul Kinlan and I talking about building that first "Hello World" kind of application in this screencast recorded from a live webinar that we did several weeks ago.

You will also want to check out the Pkg Plugin, which is a free jQuery plugin that makes it possible to work within the confines of Packaged Apps security restrictions.

Step 3. Build Something Awesome

Once you get the hang of how a Packaged App goes together, you will be astonished at how easy it is to build your application and how fast you can iterate thanks to the Chrome Developer Tools.

Getting the in-depth training from these hackathons will take you that extra mile and give you the opportunity to talk directly with the Google team as well as master the Packaged Apps API's.

See You There

We plan on being at these events, and we hope that you will as well if you are in the area or able to attend.

Download Kendo UI today and build your first Chrome Packaged App. Then head over to the Google Hackathon and take that app to the next level.

About the Author
is a web developer living in Nashville, TN. He enjoys working with and meeting developers who are building mobile apps with jQuery / HTML5 and loves to hack on social API's. Burke works for Telerik as a Developer Evangelist focusing on Kendo UI. Burke is @burkeholland on Twitter.

Announcing the Kendo UI Q3 2012 Service Pack

$
0
0

Over the last few months, we’ve been working hard to deliver you an awesome Q1 2013 release (which you can read more about, here). Along the way, we figured today was time to drop a Service Pack for Q3 2012, which includes a bunch of new features and a boatload of fixes. In this blog post, I’ll highlight a few of things we’ve added in this release. To download the Service Pack right away, head over to the downloads page of your account. This post won’t cover everything new and improved, so to read more, be sure to check out the change logs:

The Kendo UI Q1 2012 service pack provides a number of updates, fixes, and enhancements to the version of Kendo UI we shipped in November of 2012. From a high-level perspective, we accomplished a great deal of work for this latest milestone:

  • 76 bugs fixed
  • 19 enhancements added
  • 48 issues resolved for Kendo UI Web
  • 8 issues resolved for Kendo UI DataViz
  • 20 issues resolved for Kendo UI Mobile
  • and much more!

Here are just a few of the cooler fixes and enhancements we’ve implemented in this service pack:

New! Check for changes in the DataSource

With this Service Pack, we added the ability to call hasChanges() on the Kendo DataSource, which returns true if the underlying data has changed since the last read.

var ds = new kendo.data.DataSource({
schema: {
    model: { id: "id" }
},
data: [{ id: 1, name: "Burke" }]
});
ds.read();

output.innerText = ds.hasChanges() // Returns false
ds.get(1).set("name", "Brandon"); // Change some data
output.innerText = ds.hasChanges(); // Returns true

 

New! Support for canceling the requestStart event

Another addition we recently made to the DataSource is the ability to cancel the requestStart event of the DataSource by calling e.preventDefault() this can be useful in cases where you want to prevent a refresh on the DataSource under certain conditions, as illustrated in this completely contrived example:

var ds = new kendo.data.DataSource({
    data: [{name: "Brandon"}, {name: "Burke"},
           {name: "Derick"}, {name: "Todd"}],
    requestStart: function(e) {
        if (dontRead.checked) { // Don't refresh if checked
            output.innerText = "Read Canceled";
            e.preventDefault(); // Cancel the refresh
        }
    }
});

ds.read();

 

New! Editor Link dialog now supports localization

With the Service Pack, we’ve added three new additional properties to the Editor messages options literal that allow you to control the input labels on the link dialog. So, if I wanted to localize the labels in another language, say Urdu, I could add the following options to my editor initialization:

$("#editor").kendoEditor({
    messages: {
        linkWebAddress: "ویب ایڈریس",
        linkText: "متن",
        linkToolTip: "اوزاری ٹوٹکا"
    }
});

 

Then, when a user clicks on the Link button, the dialog labels are now localized, as shown in the following image.

Localized Editor
Localized Editor

New! Additional widget configuration options for MVC Wrappers users.

We added a lot of new features in this quarter that will be of particular interest to our MVC Wrappers customers. These include support for deferring widget initialization scripts outside of the widget wrapper, the ability to specify aggregate member names on the grid, a configuration builder for setting column filter options on the grid, and series highlight configuration options on DataViz charts. Be sure to check out the Demos and you SP bits to try these features out.

New! Additional DataViz demos, features and enhancements In addition to adding some additional demos to our recently-release Stock Chart widget, we’ve also added some new features to DataViz charts and gauges, including series highlight configuration options, the addition of selectStart, select and selectEnd events on charts and additional data attributes for radial and linear gauges.

New! PopOver open event handler provides target element data.

In addition to a number of fixes we added to Kendo UI Mobile, we’ve added target element information to the PopOver open event, which returns the DOM element that the user clicked on to open the PopOver widget:

popOver = $('#popOver').data("kendoMobilePopOver");
popOver.bind("open", function(e) {
  console.log("Opened by: " + e.target[0]);
});

 

There’s a lot more great stuff in the Service Pack, so head on over to the the downloads page in your account and get the bits now!

What’s next (and an announcement)

While you digest everything new in the Service Pack, we’ll keep on working on the Q1 2013 release, which you’ll get to see more of as we approach the next two milestones:

  • Kendo UI Q1 2013 Beta, expected in late February
  • Kendo UI Q1 2013 Release, expected in Mid-March

We’ve already talked about a lot of what’s coming in the Q1 release, but we’ve also got a few surprises up our sleeves. Today, I’m happy to share one of these with you….

The Q1 2013 release of Kendo UI will include support for TypeScript!

As many of you know, TypeScript is a compile-to-js maintained by Microsoft. The language is still very-much in an Alpha state, but we’ve gotten so much demand from our customers, that we decided to start offering support (in the form of TypeScript definition files for Kendo UI ) sooner, rather than later. If you’re a TypeScript fan-boy or -girl, keep an eye out for those in the Beta timeframe.

These are just a few of the updates, fixes, and enhancements that you’ll find in the service pack for the Kendo UI Q3 2012 release. Many of the bugs and feature requests we implemented came directly from you; our customers. We spend a great deal of time tracking your feedback on our forums, our UserVoice site, Stack Overflow, Twitter (@KendoUI) and elsewhere online. As always, we welcome all feedback. Please continue to submit your requests. We’ll continue to make Kendo UI more awesome.

About the Author
(@BrandonSatrom) is Program Manager for Kendo UI and is based in Austin, TX. A unapologetic lover of the web, Brandon loves to talk about HTML, JavaScript, CSS, open source and whatever new shiny tool or technology has distracted him from that other thing he was working on. Brandon loves writing and speaking and loves hanging out with and learning from other passionate developers, both online and in person. He blogs on occasion at UserInExperience.com

Easy application layout with Twitter Bootstrap

$
0
0

You have probably seen all these nifty Twitter Bootstrap style applications popping up. You know, the ones with the black header menus and blue buttons? If you haven't seen or heard of Twitter Bootstrap, it's a base CSS / UI framework that makes laying out your application quick and easy.

Why is layout so hard?

If you have ever built a web application, you know that the web has winformsbeen somewhat limited up until recently when it comes to laying out an application as opposed to text. This is because the web and web standards were originally designed for displaying documents, not applications.

As a result, laying out columns, fixing headers in a specific location and causing content to expand and collapse with the page has been a combination of creativity and downright brutal hack. Go ahead and do a search for "2 column CSS layout". There is an absolute bevy of different methods, war stories and survivor support groups. Some of the CSS out there gets really verbose and suddenly it all starts to feel like a bad idea.  WinForms starts looking pretty good again.

Enter CSS grids

To solve this problem, people started to create "grid" layouts for CSS.  This is just the concept of laying things out in rows and columns.  Of course, there is no "grid" HTML element and using tables has been deemed a crime against humanity. The new Flexbox HTML spec seeks to resolve some of this, but it's only available in the most modern of browsers.

The CSS Grid systems accomplish element layout in columns and rows by setting widths, margins and floats for div elements. The most famous of these grid systems has historically been the 960 Grid. So named because it is based on an application width of 960px.

More and more grid systems and base CSS packages began to emerge and there were a lot of them. Blueprint was quite popular for a while, and Skeleton earned quite a reputation as well. However none of these systems were widely embraced and most were limited in their function to just page layout.

Twitter Bootstrap

Twitter Bootstrap was introduced in August 2011 after a small group of developers at Twitter realized that the current state of affairs in grid systems had led to inconsistencies and high maintenance. As of the time of this writing, it is the most popular and forked project on GitHub - outpacing both jQuery and Node.

It is not just a CSS grid system, but an end-to-end framework for quickly getting an application up and running with a consistent look and feel. If you are like me and you are not really a designer, this is very good news.

Twitter Bootstrap with Kendo UI

One of the things that Kendo UI was not designed to address is application layout (with the exception of the mobile framework which does provide this).  This is where Twitter Bootstrap comes in. We like it so much that we created a new Bootstrap theme for Kendo UI so your widgets will look seamless when used with Twitter Bootstrap itself.

Getting started

Lets take a quick look at how to get your application running quickly with Kendo UI and Twitter Bootstrap. You are going to need Kendo UI and Twitter Bootstrap included in your page (of course).  After you download Kendo UI, you will want to download Twitter Bootstrap and include its JavaScript and CSS files. The minified CSS and JavaScript files are meant for production while the uncompressed files are intended for development.

Base markup for Kendo UI / Bootstrap application

<!DOCTYPE HTML>
<html lang="en-US">
<head>
  <meta charset="UTF-8">
  <title>Kendo Bootstrap</title>
  <link href="styles/kendo.common.min.css" rel="stylesheet">
  <link href="styles/kendo.default.min.css" rel="stylesheet">
  <link href="styles/bootstrap.min.css" rel="stylesheet">
</head>
<body>

  <script src="js/jquery-1.8.2.min.js"></script>
  <script src="js/kendo.all.min.js"></script>
  <script src="js/bootstrap.min.js"></script>

</body>
</html>

 

Fixed and Fluid layouts

Twitter Bootstrap has 2 main types of layouts - Fixed and Fluid.

Fixed Layout

The Fixed layout is based on an application width of 940px. It will be centered in the browser and will never be any smaller than 940px unless you include Bootstrap’s responsive CSS files. All of the CSS for the Fixed layout will be based on absolute pixel sizes. To create a Fixed layout, create a div with a class of container.

Fixed layout

<div class="container"></div>

 

Fluid layout

The Fluid layout will make your application as wide as the browser and will resize it as the browser is resized. In this layout, all the bootstrap elements are sized by percentage. A Fluid layout is created by adding a div with a class of container-fluid.

Fluid layout

<div class="container-fluid"></div>

 

Rows and columns

Once you have defined the container, the nuts and bolts of laying things out really boils down to rows and columns.

Rows

Rows are defined simply by adding a class of row to a div. This will create a row (surprise!) spanning the width of the container.

Columns

Columns are defined by adding a class of span[number] to a div, where [number] is an integer between 1 and 12 that defines how many columns you want to span. Bootstrap rows are broken up into 12 columns.

Sample Application

Lets create a sample application with our newfound Bootstrap skills. It's going to be a simple app that searches the Flixster movie database. We will use the Fixed layout, keeping in mind that this means our application will be 940px wide. In our case, we will create the "holy grail" 2-column layout. The left side will contain the search input and the right side will have the results which we will display in a Kendo UI ListView.

The first thing that we are going to need is one of those nifty Twitter Bootstrap headers.

The Header

You could use a Kendo UI Menu here if you wanted to, but using the Twitter Bootstrap Navbar is fine for most scenarios. Now we only have one page, but we could pretend we have a bunch more. For the sake of demonstration I'll include two links in the header. One that goes nowhere, and one that goes to the Rotten Tomatoes website.

This is where Twitter Bootstrap is going to immediately start to shine. We want to position the header at the top of our page. Using Bootstrap, this is trivial.

Basic bootstrap header

<div class="navbar navbar-static-top">
  <div class="navbar-inner">
    <div class="container">
      <a class="brand">
        <img class="logo" src="http://images.rottentomatoescdn.com/images/trademark/flixster-logo.png" /> 
        Search
      </a>
      <ul class="nav">
        <li class="active">
          <a href="#">Home</a>
        </li>
        <li>
          <a href="http://www.rottentomatoes.com">Rotten Tomatoes</a>
        </li>
      </ul>
    </div>
  </div>
</div>

 

You start with a div with a class of navbar and then create an inner container which will hold the items.  Notice also that I gave the top div a class of navbar-static-top.  This is because Navbar’s can appear anywhere.  That class fixes it at the top and scrolls with the content.  If you wanted a header that didn’t scroll with the content, you would use the class navbar-fixed-top.  All of the items are then wrapped in navbar-inner class and then  a container div which sets the width of the portion of the navbar that contains your items. The navbar itself will stretch the entire width of the screen in this scenario.

The brand anchor creates a nice slightly-larger area where your logo or site title would go. You usually make this a link as well that goes back to your home page.

Adding links/buttons to your Navbar is as easy as creating a unordered list with a class of navbar and then dropping some items and anchors in it. You will want to give the selected link a class of active which will make it appear "pressed".

Lets have a look at this app so far:

If you wanted to get that black header instead of the default white one, you can add a class of navbar-inverse to the top div with a class of navbar.

That gives us a really nice header with logo included per Flixster’s terms of use.

Laying out the body

Let's get down to laying out our 2-column masterpiece. Keep in mind that we are dealing with a 12 column system. This means that we need to figure out how many columns we want our left column to span and how many the right column should span. I think that 4 columns on the left is good, which means we can allocate the rest to the right-hand side.

2 Column layout

<div class="container">
  <div class="row">
    <div class="span4">
      <h3>Search</h3>
    </div>
    <div class="span8">
      <h3>Search Results</h3>
    </div>
  </div>
</div>

 

It's too big for me to embed in this page, so click here to see the results of that simple 2 column layout.

Bootstrap offers some really neat UI elements like rounded corners on TextBoxes and Buttons. We could just create a plain and boring input and button, but we can use some of the Bootstrap styling to make it look really nice.

Styled search box and button

<div class="form-search">
  <div class="input-prepend">
    <button data-bind="click: search" class="btn">Search</button>
    <input type="text" data-bind="value: term" class="span2 search-query" name="term">
  </div>
</div>

 

The form-search class will join our button and input together so it looks like one control. The input-prepend puts a flat edge on the end of the button and the front of the input. A class of input-append does the opposite, although you would need to move the button after the input for things to look right. You can see that I have a data-bind attribute for the input as I'm using a ViewModel to handle events and configuration.

The last step is to add our ListView. We'll add pagers at the top and bottom as well. We'll also need an item template, so I’ve added one in the code below.  We just need to display the movie poster, the title, the characters and the synopsis.

ListView And Pagers

<div class="span8">
  <h3 data-bind="html: title"></h3>
  <div id="results" data-bind="visible: hasResults">
    <div data-role="pager" data-bind="source: results" id="pager" data-auto-bind="false"></div>
    <div class="results row" data-selectable="true" data-pager="pager" data-auto-bind="false" data-scrolling="virtual" data-role="listview" data-bind="source: results" data-template="template"></div>
    <div data-role="pager" data-bind="source: results" id="pager" data-auto-bind="false"></div>  
  </div>
  <div id="empty" data-bind="invisible: hasResults">
    <p>No results...</p>
  </div>
</div>
<!— template for listview items –>
<script type="text/x-kendo-template" id="template">
  <div class="result">
    <div>
      <img class="poster" src="#: posters.original #" />
    </div>
    <div class="details row">
      <p class="title">#: title #</p>
      <p>
        # $.each(data.abridged_cast, function(index, item) { #
          # if (index < data.abridged_cast.length - 1) { #
            #:this.name#,
          # } else { #
            #:this.name#
          # } #
        # }); #
      </p>
      # if (data.synopsis.length > 0) { #
        <p class="synopsis"><b>Synopsis: </b>#: synopsis #
      # } #
    </div>
  </div>
</script>

 

You can see I've done some additional MVVM binding to make sure that the Search Results title stays updated with the term we are searching for. Also, I am hiding the entire search result set and displaying some empty data based on a boolean value in the ViewModel. MVVM really helps you maintain the UI by only having to worry about values in your code. Otherwise we would be wiring up a bunch of jQuery selectors and methods that would make our code start to smell bad.

JavaScript code

(function ($) {

  // define the flixster API url so we can use it later
  var flixster = "http://api.rottentomatoes.com/api/public/v1.0/movies.jsonp?apikey=2w6rma75ygrbrnubvu7g5asc";

  var viewModel = kendo.observable({
    title: "Search Results",
    term: null,
    results: new kendo.data.DataSource({
      transport: {
        read: {
          url: flixster,
          dataType: "jsonp"
        },
        parameterMap: function(options, operation) {
          // add the appropriate paramters the query string for flixster
          if (operation === "read") {
            return {
              q: viewModel.get("term"),
              page_limit: 10,
              page: options.page
            }
          }
        }
      },
      schema: {
        data: "movies",
        total: "total"
      },
      change: function() {
        // set the title and hasChanges property
        viewModel.set("title", "Search Results For '" + viewModel.get("term") + "'");
        viewModel.set("hasResults", this.view().length > 0);
      },
      serverPaging: true,
      pageSize: 10
    }),
    search: function(e) {
      // populate the listview when the search button is clicked
      this.get("results").read();
    },
    hasResults: false       
  });

  // bind the viewmodel to the body and create all widgets
  kendo.bind(document.body, viewModel);

}(jQuery));

 

And this is our finished product. JSFiddle puts some white space around the iFrame so that's why there is that bit of a buffer at the top.

I added a few styles for the ListView, but nothing dramatic. Mostly just spacing and removing the border because I don't like it for this application.

Work smarter not harder

Twitter Bootstrap is a great way to get really good cross-browser layouts in your application without much work required on your part in terms of CSS. I have never been a huge fan of CSS and I was always annoyed by the amount of CSS tom foolery it took to get some simple things done. Twitter Bootstrap wraps that nonsense up in a nice package and lets you concentrate on building your application.

Kendo UI works great with Twitter Bootstrap with the new Bootstrap theme. Grab a copy of Kendo UI And Twitter Bootstrap and take the pain out of the parts of application development that should be easy, but just aren’t.

Next week I'll take this same application and make it responsive so that it works on mobile devices. I'll show you how to use responsive design with Kendo UI and Bootstrap so that your application look amazing at any size.

About the Author
is a web developer living in Nashville, TN. He enjoys working with and meeting developers who are building mobile apps with jQuery / HTML5 and loves to hack on social API's. Burke works for Telerik as a Developer Evangelist focusing on Kendo UI. Burke is @burkeholland on Twitter.

Responsive Design with Twitter Bootstrap

$
0
0

Last week we looked at how Twitter Bootstrap is the perfect compliment to Kendo UI in that it helps you easily lay out your application while taking care of all the tricky CSS that may be happening under the covers. I created a sample 2 column application with a fixed header that allows a user to search the Rotten Tomatoes for movies.

The application looks great, but it has a serious limitation. It only really looks great at 940px. Yes, it technically works on tablets and phones, but it doesn't look very good and it's not usable with all the buttons and links being so small. We know that we can increase the reach of our application significantly if we do some things to help it function more like a mobile application at smaller sizes. To do that, we are going to be doing a little responsive design today.

Responsive Design

The term was originally coined by Ethan Marcotte in his article at A List Apart and later in a book on the same subject. Responsive design is fundamentally the concept of changing the way your application looks and behaves as the screen size gets smaller. This is done using CSS3 media queries.

Media Queries

Media query is a really fancy term for "conditional CSS rule". It simply apples some CSS based on certain conditions set forth. If those conditions are met, the style is applied.

Media queries are supported in IE9+ and pretty much every other browser, including mobile where they are particularly applicable. If you want to get simple Media Query support for IE7 and 8, you can use the popular Respond.js pollyfill.

Let's look at a simple media query to get started:

Simple media query

<style>
@media all and (max-width="1024") {
  h2 {
    color: green;
  }
}
</style>
<h2>My color changes when the width gets below 1024px</h2>

 

Media queries have two parts, a device specification and then a size rule. In the above, we are setting the following rule...

For all devices no matter what kind, if the width of the screen gets smaller than 1024px, make the h2 text color green.

Go ahead and check it out here for yourself if you are on a desktop where you can resize the screen. Just start resizing the browser window. Once you get below 1204px, the text will change colors. Notice that when you go back past 1024px in width, the style is no longer applied - and so effectively removed.

Making our application responsive

Twitter Bootstrap includes a response.css file which will take care of Bootstrap's stake in our little application's success. Go ahead and add that file to your application's header underneath the bootstrap link.

Include responsive design support for Bootstrap

<meta charset="UTF-8">
<title>Fixster Search</title>
<link href="css/kendo.common.min.css" rel="stylesheet">
<link href="css/kendo.bootstrap.min.css" rel="stylesheet">
<link href="css/bootstrap-min.css" rel="stylesheet">
<link href="css/bootstrap-responsive-min.css" rel="stylesheet">

 

It will collapse the columns and then stack them on top of each other when the screen size gets small enough. But we are going to have to implement a few other items from the Bootstrap arsenal, as well as a few of our own media queries to get things looking just right.

The navbar

The navbar is currently not a problem for us because it doesn't contain very many items. For the sake of showing you how to deal with a small screen and a large header, I have added a few items to the navbar.

With the bootstrap-responsive CSS file, the navbar starts stacking the menu items on top of each other. When the screen gets real small, it's just a vertical list of items. This works, but it's far from ideal. We can handle this better using Bootstrap's Navbar toggle buttons.

The first thing that we need to do is wrap our unordered list of links in a div that has the nav-collapse class. This class will hide the list of links when the display gets too small to display them all horizontally.

Modify navbar to collapse links

...
<!-- wrap the list of header links in a nav-collapse class.
     this will hide the links when the browser width gets too small. -->
<div class="nav-collapse">
  <ul class="nav">
    <li>
      <a href="#">Home</a>
    </li>
    <li>
      <a href="#">Details</a>
    </li>
    <li>
      <a href="#">Settings</a>
    </li>
    <li>
      <a href="#">Profile</a>
    </li>
    <li>
      <a href="http://www.rottentomatoes.com">Rotten Tomatoes</a>
    </li>
  </ul>
</div>
...

 

We now need a way to display those links for smaller screens. Bootstrap provides a drop down menu where the links will magically appear if we just add a another element that will serve as the button to open said menu.

Add Menu Expand Button

<!-- this creates a button with 3 lines indicating that a menu is there
     if the button is pressed -->
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
  <span class="icon-bar"></span>
  <span class="icon-bar"></span>
  <span class="icon-bar"></span>
</a>
<!-- wrap the list of header links in a nav-collapse class.
     this will hide the links when the browser width gets too small. -->
<div class="nav-collapse">
  <ul class="nav">
    <li>
      <a href="#">Home</a>
    </li>
    <li>
      <a href="#">Details</a>
    </li>
    <li>
      <a href="#">Settings</a>
    </li>
    <li>
      <a href="#">Profile</a>
    </li>
    <li>
      <a href="http://www.rottentomatoes.com">Rotten Tomatoes</a>
    </li>
  </ul>
</div>

 

You can see this in action here. As you resize the width of the window, the links will disappear and the collapsed_headerbutton will appear. Clicking on the button will expand the content showing the links that were displayed horizontally when there was room enough to do so.

Now our navigation looks good, and the first column gets stack on top of the second when the window gets too small. That's an example of using some of Bootstrap's responsive capabilities.

 

The ListView

Our next order of business is to handle the Kendo UI ListView. As a starting point, let's see what it looks like full sized:

listview_full

The first thing that I notice as I start to collapse the page is that at around 980px, things start to look rather bad...

problem1

If you keep resizing, eventually the columns get stacked and the issue goes away. But as you resize down to phone width, it comes back again.

Note: I use a Chrome Plugin called Window Resizer which not only resizes your windows at set intervals, but also tells you what the current width of the browser window as as you resize it. I highly recommend this plugin.

In order to get rid of this problem, we need to do two things if there isn't enough room:

  1. Don't display the numbers between the pager buttons
  2. Hide the synopsis

Hiding the pager page list

We need to find out exactly what it is we need to hide to get the page numbers in the pager to go away. To do that, I'm going to be using the Chrome Developer Tools. I did a screencast on these recently. I can't urge you enough to become confident and comfortable with your browser's dev tools. If you don't like the dev tools that your browser has, find a browser that has some that you do like. You can always go back and test on your deployment target.

If I right-click the pager buttons, I can see that they are just an unordered list of items. That unordered list has a class of k-pager-numbers.

k-pager-numbers

We can hide the numbers by setting a style for the k-pager-numbers class that gives it display: none. We want to do the same thing on the synopsis. The synopsis will only be shown on larger screens. That makes media query look this this:

Media query to hide pages and synopsis

@media all and (max-width: 980px) {
  .k-pager-numbers, .synopsis {
    display: none;
  }
}

 

Now when we resize the window, the page numbers and synopsis go away when we are at smaller resolutions. Now the application fits perfectly to the window at any size.

no_pages_no_synopsis

 

But wait! We aren't done yet...

We still have a problem though. When I test the site on my iPhone, the application shows up and it's tiny. I have to pinch zoom to even be able to use and then scroll around. That's no good. What's happening here?

photo 1

By default, mobile devices zoom way out on content to display the whole page. This is so you see everything, and not just a portion of the page. This is how mobile devices ensure that all pages will at least be viewable. We just need to tell mobile devices that they don't need to make this auto-correction for us. To do that, we need to add the meta viewport tag.

Meta viewport tag

<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Flixster Search</title>

 

All this does is tell our device to scale the content to 100% of it's actual size. And with that change, the application is now looking a whole lot better on phones!

photo 2

The finished product

Here is the finished application. I had to host it outside of JSFiddle as JSFiddle puts everything in an iFrame, thusly rendering my meta* tag useless.

Responsive design is not a silver bullet

Responsive design is such an attractive option. It allows you to build one application that reaches all users no matter what device they are on. However, responsive design can be very tricky. Media queries are incredibly powerful, but effectively implementing a solution is going to require some solid planning and execution. RD is a strategy, not a silver bullet.

For more examples of responsive design, head over to Six Revisions and checkout their 25 examples.

Kendo UI provides you with choices when it comes to mobile. You can use responsive design with Kendo UI Web, or you can specifically target mobile with Kendo UI Mobile. Both of these strategies are valid and effective, but each has its place depending on your requirements.

Make sure that you think "mobile first" when building your application. The days of delivering content on a desktop only are a thing of the past. Kendo UI gives you the tools to build first class web and mobile applications, no matter how you decide to do it.

About the Author
is a web developer living in Nashville, TN. He enjoys working with and meeting developers who are building mobile apps with jQuery / HTML5 and loves to hack on social API's. Burke works for Telerik as a Developer Evangelist focusing on Kendo UI. Burke is @burkeholland on Twitter.

Learning kendo.data.DataSource

$
0
0

As a new member of the Kendo UI team, I have a lot to learn. There are more controls, options, framework bits and other features found in Kendo than I had ever dreamed of while using Kendo UI in my previous projects – and we are constantly adding more.

One of those features that I haven’t used prior to joining the team is the kendo.data.DataSource. The Getting Started docs bill this as “an abstraction for using local (arrays of JavaScript objects) or remote (XML, JSON, JSONP) data” which means it can do a lot to simplify data management in our JavaScript apps.

An Oversimplified “To Do” App

To begin my journey in to learning the DataSource, I built is an over-simplified “to do” list. (Note that this sample code is inspired by the TodoMVC samples, but is not intended to be a complete implementation of that project.)

You can view the full source and live version here: http://jsfiddle.net/derickbailey/9q4qv/

As you can see, this “app” is barely an app – it only does 3 very simple things:

  1. Display a list of hard-coded “to do” items
  2. Allow you to mark items as “done” or not
  3. Allow you to filter the displayed list of items based on the “done” status

but to learn about the DataSource and building an app with it, this was a good place to start.

Note that I could have used a full Kendo MVVM stack with data-bind attributes and the control suite that Kendo UI provides. But the point of this code was not to build the ultimate, perfect to-do list. I’m really only interested in the DataSource and what it can do for me. Any other code that I put in place should only play a supporting role in helping me exercise the DataSource capabilities.

The Code And Markup

There’s very little to this app. In the markup there’s a table layout to display the items and a template to render each item in to the table.

Todo Markup

<script id="template" type="text/x-kendo-template"><tr><td> <input type="checkbox" class="done" data-id="#= id #" #if(done){# checked="checked" #}#></input></td><td>
            #= id #</td><td>
          #= description #</td></tr></script><div id="example" class="k-content"><div class="demo-section"><table id="todos" class="metrotable"><thead><tr><th>Done</th><th>ID</th><th>Task</th></tr></thead><tbody><tr><td colspan="3"></td></tr></tbody><tfoot><tr><td></td><td colspan="2">
              Show: <select id="filter"><option selected="selected" value="all">All</option><option value="done">Complete</option><option value="not done">Incomplete</option></select></td></tr></tfoot></table></div></div>

In the JavaScript code, there’s a hard-coded list of “to do” items, a data source instance to work with the items, a “change” event handler to render the list of items, code to handle an item being marked as “done” or not, and code to handle filtering the items.

Todo JavaScript

  // To Do Item Template
  // -------------------

  var template = kendo.template($("#template").html());

  // To Do Item Data
  // ---------------

  // A hard coded list of items to render
  var tasks = [
    { id: 1, done: false, description: "do stuff"},
    { id: 2, done: false, description: "more stuff"},
    { id: 3, done: true, description: "this stuff to do"},
    { id: 4, done: false, description: "that stuff we need"}
  ];

  // Build the data source for the items
  var dataSource = new kendo.data.DataSource({ data: tasks });

  // When the data source changes, render the items
  dataSource.bind("change", function(e) { 
    var html = kendo.render(template, this.view());
    $("#todos tbody").html(html);
  });

  // Initial read of the items in to the data source
  dataSource.read();

  // Handle And Respond To DOM Events
  // --------------------------------

  // Handling clicking the "done" checkboxes
  $("#todos").on("change", ".done", function(e){
    var $target = $(e.currentTarget);
    var id = $target.data("id");
    var item = dataSource.get(id);
    item.set("done", $target.prop("checked"));
  });

  // Handle the filters for showing the desired items
  $("#filter").change(function(e){
    var filterValue = $(e.currentTarget).val();

    var filter = {
      field: "done",
      operator: "eq"
    };

    if (filterValue === "done"){
      filter.value = true;
    } else if (filterValue === "not done"){
      filter.value = false;
    } else {
      filter = {};
    }

    dataSource.filter(filter);
  });

The Basic DataSource Setup

At a very high level overview, this isn’t much code or markup. The process that this app flows through is equally as simple, as well.

When the app starts (using the jQuery DOMReady event, which is handled by JSFiddle in this case), I build a Kendo Template (which I've talked about before, in the context of Backbone) to render each "to do" item. I then build a DataSource using an array of simple JavaScript objects as my items.

Todo DataSource

  // To Do Item Template
  // -------------------

  var template = kendo.template($("#template").html());

  // To Do Item Data
  // ---------------

  // A hard coded list of items to render
  var tasks = [
    { id: 1, done: false, description: "do stuff"},
    { id: 2, done: false, description: "more stuff"},
    { id: 3, done: true, description: "this stuff to do"},
    { id: 4, done: false, description: "that stuff we need"}
  ];

  // Build the data source for the items
  var dataSource = new kendo.data.DataSource({ data: tasks });

Next, I set up a "change" event handler on the dataSource instance, and then "read" the data in to the dataSource.

Todo DataSource Read / Change

  // When the data source changes, render the items
  dataSource.bind("change", function(e) { 
    var html = kendo.render(template, this.view());
    $("#todos tbody").html(html);
  });

  // Initial read of the items in to the data source
  dataSource.read();

Calling “read” on the dataSource instance will tell it to read the raw data that I provided. This will trigger the “change” event from the dataSource, which tells the app to render the current view of the data and stuff the result in to the <table> in the DOM.

This “change” event handler is very simple, but also very important. We’ll see this called again when we get to the checkbox handler and filtering functionality.

Handling The "Done" Checkboxes

Next, a DOM event handler is set up to listen for change events on the “done” checkboxes. When a checkbox is checked or unchecked, the id for the item is pulled from a “data-id” attribute in the DOM. This is used to load the correct item from the dataSource so that it can be updated appropriately.

Todo Item Done

  // Handling clicking the "done" checkboxes
  $("#todos").on("change", ".done", function(e){
    var $target = $(e.currentTarget);
    var id = $target.data("id");
    var item = dataSource.get(id);
    item.set("done", $target.prop("checked"));
  });

Changing the “done” value for a model that has been pulled from the dataSource will cause a “change” event on the dataSource itself. This will cause the change handler from above to fire, re-rendering the list of items in to the DOM.

A Brief Detour In To jQuery And DOM Events

It is important to note how the “change” event is set up for the checkboxes in this code. When I first put this in place, I was doing this:

Bad jQuery DOM Event

$(".done").change(function(e){
  // ...
});

But I found that this code only worked the first time the page rendered the list of items. After I filtered the list and it re-rendered (which I’ll cover in a moment), the check box change events no longer worked. This is because the jQuery selector used to find the checkboxes only runs once when the app starts. That means the checkboxes that were originally found on the screen would have the event handler set up, but re-rendering the list produces new checkboxes which means they will not have the event handler.

There are two basic solutions for this problem:

  1. Re-bind the checkbox event handler after every rendering of the list
  2. Use jQuery’s DOM event delegation

I chose option #2 for two reasons:

First, re-binding the checkbox handlers would cause the code to be a bit more ugly than I want. I don’t want to set up the checkbox click handlers inside of my dataSource’s “change” event handler because of the nested callbacks that this would cause. Nested callbacks are not always bad, but they quickly lead to code that is difficult to read, understand and maintain.

And second, if the list of items was large enough, it would cause performance problems to bind a change event to every checkbox. Instead, I want one change event that knows to listen to any checkbox.

Using jQuery’s DOM event delegation solves both of these problems as well as the original problem of the events not firing after re-rendering the list. To do this, I find the <table> element that will eventually contain the checkboxes, using a jQuery selector. Once I have that element, I attach a “change” event listener and use another selector to specify that this change event should fire when any checkbox in the table triggers a change event.

jQuery is smart enough to set up the event handler so that it will receive all change events from any checkbox in the table, no matter how many times I re-render the list of items and checkboxes for the them. For more information on this, see jQuery’s documentation for the “on” method.

Filtering The List Of Items

The last chunk of code in this little app is for filtering the list of items. You can choose from “All” to show all of the items, “Done” to only show the items that are done, and “Not Done” to only show the items that are not done. To do that, I set up a “change” event on the select box and handle configuring the DataSource filter in that event.

Todo Item Filter

  // Handle the filters for showing the desired items
  $("#filter").change(function(e){
    var filterValue = $(e.currentTarget).val();

    var filter = {
      field: "done",
      operator: "eq"
    };

    if (filterValue === "done"){
      filter.value = true;
    } else if (filterValue === "not done"){
      filter.value = false;
    } else {
      filter = {};
    }

    dataSource.filter(filter);
  });

In this event handler, I’m setting up a basic set of options for the filtering – telling it what field to filter on, and what operator to use for the filter. In this case, I’m checking to see if the “done” flag on my items are “eq” (equal) to … a value yet to be determined.

Once I have the basic configuration set up, I check the filter value from the select box. If the value is “done” or “not done”, I set the filter.value appropriately. If the filter is set to “all”, though, I wipe out the filter configuration entirely. This effectively means no filtering should be done.

After setting up the filtering configuration, I call the dataSource.filter method and pass the configuration to it. This call triggers a “change” event on the dataSource object, which re-renders the list of items on the screen. When the dataSource “change” fires in this case, the “ds.view()” method returns the list of items that match the current filters, paging, sorting and other criteria set up on the dataSource instance. This is what allows the rendered list to only show the filtered items.

As I noted above, checking or un-checking the “done” field for any of the items will trigger a “change” event on the dataSource. The combined effect of this with the dataSource.filter means that any time you are showing a filtered list of items (other than “All”), changing the “done” status of an item will cause that item to disappear from the screen. This happens because the item’s data has been updated and it no longer matches the current filter criteria. To see this item again, switch to the other filter or remove the filter by selecting “All”.

And So Much More ...

The DataSource does far more than I could learn in a day or cover in a single blog post. I’ve only touched on the surface of what it can do in this post, with a list of “to do” items that can’t be added to or have any removed. Be sure to watch this blog for more information, though. As I continue to learn more about this framework piece, I’ll continue to post on what I am doing with it, why I am doing it that way, and how I am getting it done.

About the Author
is a Developer Advocate for Kendo UI, a developer, speaker, trainer, screen-caster and much more. He's been slinging code since the late 80’s and doing it professionally since the mid 90's. These days, Derick spends his time primarily writing javascript with back-end languages of all types, including Ruby, NodeJS, .NET and more. Derick blogs at DerickBailey.LosTechies.com, produces screencasts at WatchMeCode.net, tweets as @derickbailey and provides support and assistance for JavaScript, BackboneJS, MarionetteJS and much more around the web.

Adding And Removing Items In kendo.data.DataSource

$
0
0

In my previous post I walked through the basic steps to use a kendo.data.DataSource to build a very simple "to do" list. That version of the app was mostly read-only, with the exception of being able to mark an item as done. You couldn't, however, add or remove any items. It turns out adding and removing is as easy as anything else that has been done in this app, though, and it only takes a few lines of markup and JavaScript to add these features.

The Todo App

Here's what my app looks like with the add / remove features in place:

The complete code can be found in this JSFiddle

Once again, I want to note that this application is inspired by the TodoMVC app demos, but it is not intended to be a complete or canonical representation of TodoMVC or Kendo UI. I'm building a demo that is meant to exercise the Kendo DataSource object in a meaningful way, so that I can learn more about this framework peice and hoepfully help others better understand what it can do for us.

Additional Markup

There have been a few changes to the markup, as you can see from the output. I've added a <frameset> to the page to provide an area to add new "to do" items. I've also adjusted the table slightly and added a "remove" button below it, to allow us to remove items that are already done.

Todo Markup

<fieldset class=".add-new">
        <legend>Add A Todo</legend>
        <p>Enter a to do item description</p>
        <input name="description" />
        <button id="add">Add</button>
    </fieldset>

    <!-- ... -->

    <tr>
        <td>Actions: </td>
            <td colspan="2">
                <button id="remove-done">Remove Done Items</button>
        </td>
    </tr>

This is simple markup with no Kendo templates at this point. The important points are the input ID's, which we will use in our JavaScript so that we can add new "to do" items or remove the ones that are complete.

Adding A New To Do Item

Handling the addition of a "to do" item is done with a jQuery selector and a DOM "click" event for the "add" button. When we receive that event, we read the "to do" description from the input box. Then we build an object literal that contains all of the fields we need: an id, a "done" status and the description we entered.

Adding A To Do Item

  // Handle adding a new to do item
  $("#add").click(function(e){
    e.preventDefault();

    var $todo = $("input[name='description']");

    currentId += 1;
    dataSource.add({
      id: currentId,
      done: false,
      description: $todo.val()
    });

    $todo.val("");
    $todo.focus();
  });

Adding a "to do" item to the DataSource is very simple, as you can see. You only need to call the add method on the dataSource instance, and pass in the object to be added.

To create the "to do" items with a unique ID, I've added a "currentId" variable to this code. This is used as a simple counter to give me an incremental ID to attach to new "to do" items, which is used to determine the item that is being marked as done (see the previous post for more information on that) when clicking a "done" checkbox. The currentId variable increments prior to every use, ensuring that we have a unique ID for each item we add.

I'm also clearing out the description input box after the item has been added and then re-focusing the browser that input box. This allows me to add a number of "to do" items faster as it reduces the number of clicks needed to create one and re-focus on the text input field.

Clearing The Done Items

Clearing the items that are already "done" takes a little more work than adding an item. There are no methods on the dataSource to bulk-remove items, at this time. That means I need to call the .remove method to remove the individual items that are "done".

Now, the question is: How do I get the list of items that are done?

The first idea that I had was to use the .filter method to filter down the list, the way I am doing for the filtering drop list. However, this method doesn't return anything to me. It just sets up some filters and triggers some events so that I can call the view method to get the list of items that match the filter. This sounded like a good idea at first, until I remembered that calling .filter(...) would trigger the "change" event and cause the item list to re-render. The net result of that is the view would re-render itself with the list of "done" items. This doesn't sound too bad off-hand, until I account for the different filter states for the view. If I'm looking at "all" items, I don't want to filter down to just the "done" items so I can delete them. I want to just get rid of them without re-rendering the list down to the items that will be removed first.

The solution I cam up with needs a little more code, then, because of the way the filter method works. I have to read the raw data from the dataSource, iterate over the array that this method returns, and check the "done" status of each item in the array. As I find items that are "done", I can then remove them from the dataSource.

Removing "Done" To Do Items

  // Handle removing cleared items
  $("#remove-done").click(function(e){
    e.preventDefault();

    var raw = dataSource.data();
    var length = raw.length;

    // iterate and remove "done" items
    var item, i;
    for(i=length-1; i>=0; i--){

      item = raw[i];
      if (item.done){
        dataSource.remove(item);
      }

    }

  });

The .data() method, unlike .view() will return the raw data from the underlying data store, unfiltered, unsorted, un-paged. It just hands the array of objects back to me in order to let me do what I want with them. This gives me the ability to look at all of the items in the dataSource instead of just the filtered, sorted and paged list that .view() would return.

Once I have that raw list, I set up a loop to iterate the length of the array. Within that for-loop, I grab the "to do" item for the index that I'm currently on and check to see if it is "done" or not. If it is, I tell the dataSource to remove that item.

Iterating Backwards

Did you notice that I'm looping through the list backwards?

Inverse For-Loop

  var i, length=data.length;

  for (i=length-1; i>= 0; i--){
    // ...
  }

Instead of the usual for-loop counting from 0 to a length, I am counting from length-1 back down to 0. I'm doing this because as I iterate through the array, I am telling the dataSource to remove the items. Since the dataSource handed me a reference to the array that it holds, this means I'm removing items from the array that I'm iterating. If we count from 0 to length, removing an item would throw off the iteration and cause errors. We would skip every other item and we would end up counting past the end of the array due to the length being modified as I go. But by counting backwards - from the end of the list back to the beginning - I can avoid these problems. Counting down to zero will always get to zero, no matter how many items I remove.

Editing Items And Other Features

I havent set up an edit feature for the "to do" items yet, other than allowing them to be marked as "done". It should be fairly simple to put this in place, but I'll leave that up to you as an exercise to complete. How would you set up the edit form vs add form: would you re-use the same form, or have a different form for that? What would you have to do with the dataSource and the items within in to facilitate editing?

While this little "to do" application is not terribly feature-rich or beautiful, it is giving us a good idea of what the DataSource can do for us. But there's much more that it can do, than what we have seen so far. For example, the DataSource can manage remote data sources for us - reaching out to a server somewhere, to read, write and update data as needed. I'll dig in to remote data in a future post, and show the basics of what it takes to read and write data to a server.

About the Author
is a Developer Advocate for Kendo UI, a developer, speaker, trainer, screen-caster and much more. He's been slinging code since the late 80’s and doing it professionally since the mid 90's. These days, Derick spends his time primarily writing javascript with back-end languages of all types, including Ruby, NodeJS, .NET and more. Derick blogs at DerickBailey.LosTechies.com, produces screencasts at WatchMeCode.net, tweets as @derickbailey and provides support and assistance for JavaScript, BackboneJS, MarionetteJS and much more around the web.

Kendo UI at HTML5tx

$
0
0

html5-tx Looking for a chance to spend your Saturday learning from some amazing HTML5 experts, like Christopher Schmitt, Mike Taylor (Opera), Brian Rinaldi (Adobe), our own Burke Holland, and many more? Then you better make your way to Austin, Texas tomorrow, Saturday, February 2nd, because it’s time for the next edition of HTML5tx!

Of course, an event this popular is already sold out (sorry), so hopefully you already have your ticket. If you don’t, be sure to join the fun online and follow the #html5tx hashtag and check-out videos from last year’s edition. It’s just like being there live…kinda…

Kendo UI participates in events around the globe every year (look for us this year in a city near you!), but HTML5tx is a special conference for Kendo UI for a couple reasons:

  1. Kendo UI is a top sponsor of HTML5tx this year
  2. Kendo UI is organized by Kendo UI’s own PM, Brandon Satrom (in his “free time” no less!)

We’re excited to support such a great event for HTML5 developers, and we’ll be in full force on the ground this weekend, too. Brandon, Burke, and I will be at HTML5tx, talking shop, and participating in Open Spaces. If you want to swing by and talk about Kendo UI or anything HTML5 or mobile development related, we’d love to chat. Don’t be shy. (We’ll also be on twitter throughout the day for all of you that can’t make it. Ask us questions there, too!)

If you are one of the lucky attendees at tomorrow’s event, in addition to some fun Kendo UI swag, you’ve got a chance to claim a free Kendo UI license by simply participating in the HTML5tx Hacky Hour! Just join the event at Austin’s Capital Factory and sign-up to claim your license. It’s a great way to have some fun and get some great tools, yours to keep.

And because I don’t want everyone else to feel left out, I’ll be giving away one Kendo UI Complete license this weekend to one lucky tweeter! Just tweet on Saturday with the #html5tx hashtag + the #KendoUI tag so that I can find your tweets. I’ll announce the random winner on Monday on the @KendoUI twitter account. Cool? Cool.

See you in Austin.

(Oh! And don’t forget to check-out Burke Holland’s“Defying Facebook: Creating High Performance Mobile Apps” session at 9:50 AM on Saturday. It will be epic.)

About the Author
is an avid HTML5, CSS3, and JavaScript advocate, and geek about all things web development. He is an active speaker and author, helping developers around the world learn and adopt HTML5. Todd works for Telerik as VP of HTML5 Web & Mobile Tools, where his current technical focus is on Kendo UI. Todd is @toddanglin on Twitter.


Head first into Kendo UI For Java Developers

$
0
0

Last quarter we announced the beta release of our JSP wrappers for Kendo UI. These wrappers are JSP and while our demos are built on Spring MVC, you can use the wrappers with any JSP page.

They provide a strongly typed experience by adding in a special HTML tags that allow you to fully configure a Kendo UI widget while writing a minimum amount of JavaScript. The helps you to know that your widgets are configured correctly at design time, thusly saving you valuable development cycles.

In preparation for our next release, I have been creating some sample code for how to get started with the JSP wrappers by building an application that addresses many of the common enterprise development challenges. This includes things like hierarchical data, treeviews, grids, custom grid editor fields, validation and full CRUD operations.

In this post, we'll take a look at how you can get started with the JSP wrappers, and we'll additionally create a foundation for keeping our Java code clean and maintainable

Prerequistes

The first step is of course to download the beta of the JSP wrappers if you haven't already. Keep in mind that this beta will be a full release in March, which will be upon us before we know it.

Inside that download you will see two folders. One contains the Spring demos of all the widgets. I highly suggest that you get this up and running. There is a great walkthrough here on configuring your environment to load up the demos.

The other folder is called kendo-taglib and contains the Kendo UI JSP Wrappers.

Fire up a new project

You will need Java 1.7 and Tomcat 7 for the sample project in this article.

Let's create a new Dynamic Web Project. In Eclipse, select File/New/Dynamic Web Application.

Name it EmployeeDashboard

newdynamicwebapp

A look at the project structure

In this newly created application you will see several folders. Most we are not going to touch. The two that are important are Java Resources and WebContent.

You can think of it this way: Any HTML, CSS or JavaScript is going in the WebContent directory. Any Java classes that we create are going in the JavaResources folder.

Installing the wrappers in the project

There is a single .JAR file in the kendo-taglib directory. All you need to do is drag that file into the WebContent/WEB-INF/lib directory. Go ahead and select Copy Files when you are prompted.

You will also be needing the Kendo UI CSS files, JavaScript file and jQuery.

From your Kendo UI JSP download folder, drag the styles and js folders to the WebContent folder in your project. Remember to always select Copy files and folders.

Setting up our sample data

The database is just the Northwind database and I'm using SQLite as the database so that I can distribute it to you in a working form. In order to connect this database, we need a driver. You can download the SQLite driver here. You add it to your project the same way that you did the Kendo UI JSP wrappers. Just drag the JAR into the WebContent/WEB-INF/lib.

The database itself is provided as a file in the GitHub repo for the source code from this project. It's simply called Sample.db. I have put this in the WebContent/data.

Connecting and retrieving data

Expand the Java Resources folder. Right click the src folder and select new/Package. Name it repositories.

newpackage

Right click the repostories package you just created and select new/Class.

Name the class EmployeeRepository.

employeesrepository

This class is going to be where we do all our database manipulation. We will be needing a SQLConnection object.

Add SQL Connection Object

package repositories;

import java.sql.Connection;

public class EmployeeRepository {

  // a class level sql connection object 
  private Connection _conn = null;

}

 

To properly connect to the database, we'll need to create an instance of the SQLite driver in the constructor of this class. We need to know what it's physical path on the server is. That is information that we can obtain in our servlet (which we haven't written yet), so we can just pass it in as a string to the constructor and then initialize the driver.

Initialize Driver And Connection

try {

  // initialize the database driver
  Class.forName("org.sqlite.JDBC");

  // set the connection instance
  _conn = DriverManager.getConnection("jdbc:sqlite:" + path);

} catch (ClassNotFoundException | SQLException e) {
  e.printStackTrace();
}

 

Notice that I also initialized the SQL Connection there as well.

Modeling data

Before we actually read from the database, we need to be able to create a container of employee objects. We don't have an employee object yet. This is referred to as our model. The model is simply a class that represents an employee.

Right click the Java Resources/src folder and create a new package named models.

Right click the new models package and create a new class called Employee.

This class just needs to have the same properties that an employee in the database has. In the Employees table, there are quite a few fields. For the sake of this project, we only need...

  • EmployeeID
  • FirstName
  • LastName
  • ReportsTo (We will call this ManagerID in our model)

Employee Model Properties

package models;

public class Employee {

  private int EmployeeID;
  private int ManagerID;
  private String LastName;
  private String FirstName;

}

In order to be able to set these variables to values when we use this class, we need to create getters and setters. These getters and setters are simply methods that will get and set the value of a specific property. We could type all of this out, but it's easier to make Eclipse do this for us.

Select all the private variables. Right click them and select Source/Generate Getters And Setters

Screen Shot 2013-02-04 at 2.58.52 PM

generategetterssetters

I always change the insertion point drop down so that it puts them after the last field I declared (in this case it's FirstName).

Now your class should look like this:

Employee Model Class

package models;

public class Employee {

  private int EmployeeID;
  private int ManagerID;
  private String LastName;
  private String FirstName;

  public int getEmployeeID() {
    return EmployeeID;
  }
  public void setEmployeeID(int employeeID) {
    EmployeeID = employeeID;
  }
  public int getManagerID() {
    return ManagerID;
  }
  public void setManagerID(int managerID) {
    ManagerID = managerID;
  }
  public String getLastName() {
    return LastName;
  }
  public void setLastName(String lastName) {
    LastName = lastName;
  }
  public String getFirstName() {
    return FirstName;
  }
  public void setFirstName(String firstName) {
    FirstName = firstName;
  }

}

Now we're all set to get the data from the database.

Switch back to the EmployeeRepository class.

Add a method to listEmployeees which will return a collection of employee model objects. You will need to import several classes so pay attention to the import statements at the top.

EmployeeRepository.java

package repositories;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import models.Employee;

public class EmployeeRepository {

  private Connection _conn = null;

  public EmployeeRepository(String path) {

    // initialize the database driver
        try {
          Class.forName("org.sqlite.JDBC");

      // set the connection instance
      _conn = DriverManager.getConnection("jdbc:sqlite:" + path);

    } catch (ClassNotFoundException | SQLException e) {
      e.printStackTrace();
    }

  }

  public List<Employee> listEmployees (){

    // create a new empty list of employees to return as a result
    List<Employee> employees = new ArrayList<Employee>();

    try {

      // use prepared statements to prevent sql injection attacks
      PreparedStatement stmt = null;

      // the query to send to the database
      String query = "SELECT e.EmployeeID, e.FirstName, e.LastName, e.ReportsTo AS ManagerID "+
                 "From Employees e";

      // create the prepared statement with the connection
      stmt = _conn.prepareStatment(query);

      // execute the query into a result set
      ResultSet rs = stmt.executeQuery();

      // iterate through the result set
      while(rs.next()) {

        // create a new employee model object
        Employee employee = new Employee();

        // select fields out of the database and set them on the class
        employee.setEmployeeID(rs.getInt("EmployeeID"));
        employee.setFirstName(rs.getString("FirstName"));
        employee.setLastName(rs.getString("LastName"));
        employee.setManagerID(rs.getInt("ManagerID"));

        // add the class to the list
        employees.add(employee);
      }

      // close the result set and statement
      rs.close();
      stmt.close();
    }
    catch (SQLException e) {
      e.printStackTrace();
    }

    // return the result list
    return employees;

  }

}

 

All we did here was execute a simple SQL statement against the database, iterate through a ResultSet object and map the database fields to a new employee model object, which was then added to a collection which we will return from this method.

Note that if you are working on an application of larger scale in Java, many developers will choose some sort of ORM framework to keep SQL out of their Java code. For the sake of simplicity in explanation, only prepared statements are used here.

Delivering data over the web

Now that we are able to actually get the data out of the database, we need to be able to "expose" the data on the web. This will be done with a Servlet.

Java Servlets allow you to respond to HTTP requests made from the web and determine what is returned.

Right click the Java Resoures/src directory and create an api package.

Right click the api package and select new/Servlet. Name it Employees. Edit the URL mappings so that instead of /Employees it says /api/employees. Click Finish.

A new servlet is created with methods that will respond to a GET (doGet) and a POST (doPost).

If you recall, the constructor that we created for the EmployeeRepository takes in the path the the SQLite database. We can get it's path by looking at the getRealPath method that is on the servlet context. However, we can't access it until the init method. We need to just override it and new up the repository class there.

package api;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import repositories.EmployeeRepository;


/**
 * Servlet implementation class Employees
 */
@WebServlet(description = "A servlet to return data about employees from the database", urlPatterns = { "/api/employees" })
public class Employees extends HttpServlet {
  private static final long serialVersionUID = 1L;

  // employee repository class
  private EmployeeRepository _repository = null;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public Employees() {
        super();
        // TODO Auto-generated constructor stub
    }

    public void init() throws ServletException {
      super.init();

      // create a new instance of the repository class. pass in the path to the data/sample.db
      // file which we can get by getting the servlet context, then calling 'getRealPath'
      _repository = new EmployeeRepository(this.getServletContext().getRealPath("data/sample.db"));
    }

  /**
   * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
   */
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
  }

  /**
   * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
   */
  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
  }

}

 

Returning employee data as JSON

In order to be able to return our employees data as JSON, download the Google Gson JAR and drag it into WebContent/WEB-INFO/lib.

Now just get the list of Employees from the listEmployees method in the EmployeeRepository object and use the Gson library to turn it into JSON. We then write that result to the response stream and set our return content type to JSON.

package api;

import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.gson.Gson;
import repositories.EmployeeRepository;


/**
 * Servlet implementation class Employees
 */
@WebServlet(description = "A servlet to return data about employees from the database", urlPatterns = { "/api/employees" })
public class Employees extends HttpServlet {
  private static final long serialVersionUID = 1L;

  // employee repository class
  private EmployeeRepository _repository = null;
  private Gson _gson = null;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public Employees() {
        super();

        // initialize the Gson library
        _gson = new Gson();
    }

    public void init() throws ServletException {
      super.init();

      // create a new instance of the repository class. pass in the path to the data/sample.db
      // file which we can get by getting the servlet context, then calling 'getRealPath'
      _repository = new EmployeeRepository(this.getServletContext().getRealPath("data/sample.db"));
    }

  /**
   * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
   */
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    try {

      // get the employees from the database
      List<models.Employee> employees = _repository.listEmployees();

      // set the content type we are sending back as JSON
      response.setContentType("application/json"); 

      // convert the list to json and write it to the response
      response.getWriter().print(_gson.toJson(employees));

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
   */
  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
  }

}

Since we changed the URL Mapping when we created this servlet, it will respond to requests made to server/api/employees.

If you browse to this url, you should see the employees from the database returned as JSON. In my case, this url is http://localhost:8080/EmployeeDashboard/api/employees.

employeesjson

Consuming JSON with the Kendo UI Wrappers

Now that we have our data exposed over the web, we can consume it with Kendo UI and AJAX.

Right click the WebContent folder and select new/JSP File. Name it index.jsp. Click Finish.

index

In order to use the Kendo UI Wrappers, we need to include the tag library at the top of the page, the Kendo UI CSS, jQuery and Kendo UI JavaScript files.

Once that is done, we can use the special kendo html tags that will construct widgets. We'll use a Kendo UI TreeView to display the employees.

<%@ page language="java" contentType="text/html; charset=US-ASCII"
    pageEncoding="US-ASCII"%>
<%@taglib prefix="kendo" uri="http://www.kendoui.com/jsp/tags"%>
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
  <title>Insert title here</title>
  <!-- kendo css files –>
  <link href="styles/kendo.common.min.css" rel="stylesheet">
  <link href="styles/kendo.default.min.css" rel="stylesheet">
  <!-- jquery and kendo javascript. jquery MUST be first. –>
  <script src="js/jquery.min.js"></script>
  <script src="js/kendo.all.min.js"></script>
</head>
<body>
 <kendo:treeView name="employees" textField="FirstName">
  <kendo:dataSource>
    <kendo:dataSource-transport read="api/employees"></kendo:dataSource-transport>
  </kendo:dataSource>
 </kendo:treeView>
</body>
</html>

 

The TreeView configuration requires us to set the DataSource. The DataSource has a Transport configuration that will define endpoints. We just need to read the URL that returns the JSON employee data. We also set the textField so that the TreeView knows which field in the data to display.

treeviewflat

The TreeView shows the list of employees as a flat list. It's not much of a TreeView. That's because we need to return the data in hierarchical form.

Lazy loading hierarchical data

We want to "lazy load" our TreeView. That is, we will show top level objects and as the user drills down, we will make requests for the employees with the ManagerID of the employee that was expanded in the TreeView.

To do this, the TreeView needs two important pieces of information. The first one is whether or not the current item has any children. The second is what field it should pass to the read transport to get those children.

We'll use a field called HasChildren that we will need to define. I'm also going to set the textField to FullName which is another field we will be adding to our model.

The TreeView will send it's parameters over as JSON by default even for a GET. That means we need to use the parameterMap to specify a function which will return the EmployeeID in key/value query string format. This is currently a beta requirement and won't be necessary in the official release as it is fixed internally.

<%@ page language="java" contentType="text/html; charset=US-ASCII"
    pageEncoding="US-ASCII"%>
<%@taglib prefix="kendo" uri="http://www.kendoui.com/jsp/tags"%>
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
  <title>Insert title here</title>
  <!-- kendo css files –>
  <link href="styles/kendo.common.min.css" rel="stylesheet">
  <link href="styles/kendo.default.min.css" rel="stylesheet">
  <!-- jquery and kendo javascript. jquery MUST be first. –>
  <script src="js/jquery.min.js"></script>
  <script src="js/kendo.all.min.js"></script>
</head>
<body>
 <kendo:treeView name="employees" dataTextField="FullName">
  <kendo:dataSource>
    <kendo:dataSource-transport read="api/employees">
      <kendo:dataSource-transport-parameterMap>
        <script>
          function parameterMap(options, operation) {
            if (operation === "read") {
              return { EmployeeID: options.EmployeeID };
            } 
            else {
              return options;
            }
          }
        </script>
      </kendo:dataSource-transport-parameterMap>
    </kendo:dataSource-transport>
    <kendo:dataSource-schema>
      <kendo:dataSource-schema-hierarchical-model hasChildren="HasChildren" id="EmployeeID"></kendo:dataSource-schema-hierarchical-model>
    </kendo:dataSource-schema>
  </kendo:dataSource>
 </kendo:treeView>
</body>
</html>

 

In the Employee model class, create two new fields called FullName and HasChildren. The FullName property is just a concatenation of the FirstName and LastName fields.

 package models;

public class Employee {

  private int EmployeeID;
  private int ManagerID;
  private String LastName;
  private String FirstName;
  private String FullName;
  private Boolean HasChildren;

  public int getEmployeeID() {
    return EmployeeID;
  }
  public void setEmployeeID(int employeeID) {
    EmployeeID = employeeID;
  }
  public int getManagerID() {
    return ManagerID;
  }
  public void setManagerID(int managerID) {
    ManagerID = managerID;
  }
  public String getLastName() {
    return LastName;
  }
  public void setLastName(String lastName) {
    LastName = lastName;
  }
  public String getFirstName() {
    return FirstName;
  }
  public void setFirstName(String firstName) {
    FirstName = firstName;
  }
  public String getFullName() {
    return FullName;
  }
  public void setFullName() {
    FullName = FirstName + " " + LastName;
  }
  public Boolean getHasChildren() {
    return HasChildren;
  }
  public void setHasChildren(Boolean hasChildren) {
    HasChildren = hasChildren;
  }
}
 

The TreeView will be sending over the ManagerID with each request for more employees, so we need to read that off of the request in the servlet and send it down to the EmployeeRepositorylistEmployees method. Where we can apply it in the WHERE clause of the prepared statement.

We'll also need to determine if our employee has any children. We can accomplish that by selecting a count of their direct reports, and then setting the model field to true if that count is greater than 0.

package repositories;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import models.Employee;

public class EmployeeRepository {

  private Connection _conn = null;

  public EmployeeRepository() { }

  public EmployeeRepository(String path) {

    // initialize the database driver
        try {
          Class.forName("org.sqlite.JDBC");

      // set the connection instance
      _conn = DriverManager.getConnection("jdbc:sqlite:" + path);

    } catch (ClassNotFoundException | SQLException e) {
      e.printStackTrace();
    }

  }

  public List<Employee> listEmployees (int managerId){

    // create a new empty list of employees to return as a result
    List<Employee> employees = new ArrayList<Employee>();

    try {

      // use prepared statements to prevent sql injection attacks
      PreparedStatement stmt = null;

      // the query to send to the database
      String query = "SELECT e.EmployeeID, e.FirstName, e.LastName, e.ReportsTo AS ManagerID, "+
                 "(SELECT COUNT(*) FROM Employees WHERE ReportsTo = e.EmployeeID) AS DirectReports " +
                 "From Employees e ";     

      // add a where clause to the query
      // if the employee doesn't have a manager
      if (managerId == 0) {
        // select where employees reportsto is null
        query += "WHERE e.ReportsTo IS NULL";
        stmt = _conn.prepareStatement(query);
      // otherwise
      } else {
        // select where the reportsto is equal to the employeeId parameter
        query +=  "WHERE e.ReportsTo = ?";
        stmt = _conn.prepareStatement(query);
        stmt.setInt(1, managerId);
      }

      // execute the query into a result set
      ResultSet rs = stmt.executeQuery();

      // iterate through the result set
      while(rs.next()) {

        // create a new employee model object
        Employee employee = new Employee();

        // select fields out of the database and set them on the class
        employee.setEmployeeID(rs.getInt("EmployeeID"));
        employee.setFirstName(rs.getString("FirstName"));
        employee.setLastName(rs.getString("LastName"));
        employee.setManagerID(rs.getInt("ManagerID"));
        employee.setHasChildren(rs.getInt("DirectReports") > 0);
        employee.setFullName();

        // add the class to the list
        employees.add(employee);
      }
    }
    catch (SQLException e) {
      e.printStackTrace();
    }

    // return the result list
    return employees;

  }

}

 

Now when we run the application, only the top level employee is shown - Andrew Fuller. If we expand his node, we see his direct reports. If you inspect the network requests, you will see that the request for data is not made until the node is expanded, giving us some nice "lazy loading" so that only the data we need is loaded from the server.

employeetree

Next week we'll look at how to select an item in the TreeView and populate a Kendo UI Grid with related details.

Project source code

For now you can grab the source code for this on our GitHub repo.

See you next week!

About the Author
is a web developer living in Nashville, TN. He enjoys working with and meeting developers who are building mobile apps with jQuery / HTML5 and loves to hack on social API's. Burke works for Telerik as a Developer Evangelist focusing on Kendo UI. Burke is @burkeholland on Twitter.

Wrapping A Backbone.Collection In A kendo.data.DataSource

$
0
0

In my first post about learning kendo.data.DataSource , a commenter asked me about the differences between this and a Backbone.Collection. He also wondered if there would be any value in creating an adapter that could turn a Backbone.Collection in to a Kendo UI DataSource.

The value that a DataSource wrapper provides, beyond integrating Backbone and Kendo UI from a visual perspective, is in the data-binding and other logic that Kendo UI provides in it's widgets and control suite. I've integrated them in previous projects for clients, but it has always required custom code for each situation that I was tying together. I don't like repeating code and I don't like having to manually wire things together. So it makes sense to build an adapter between a Backbone.Collection and Kendo UI DataSource.

Building A Custom Transport

The DataSource object has a lot of potential entry points for extension and customization. This can make it difficult to know exactly what to start with and what direction to head. But in looking through the documentation and demos , I noticed that there's a trend in changing how the DataSource works with different types of back-end data: a transport object.

According to the documentation, a transport

Specifies the settings for loading and saving data. This can be a remote or local/in-memory data.

This sounds like exactly what I want. Rather than having to deal with customizing the data-binding and other aspects of creating a full fledged DataSource, I can stick with the core CRUD operations of workign with my data's "back-end". In this, I'll just be pushing everything to and from a Backbone.Collection.

To get started, I dug through the documentation for transports and looked at a few examples that some co-workers sent to me. It turns out there is very little that I really need to do, though there is a lot that I could do.

To build a completely custom transport, I only need to provide an object literal to my DataSource instance, with basic CRUD operation methods:

A Custom Transport

var dataStore = new kendo.data.DataStore({

  transport: {
    create: function(options){
      // create a new entity with the `options.data` here
      // call `options.success(entity)` when I'm done
    },

    read: function(options){
      // send back all data as a simple JSON document or object literal
      // options.success(myData);
    },

    update: function(options){
      // update an existing entity with the `options.data` here
      // call `options.success(entity)` when I'm done
    },

    destroy: function(options){
      // destroy an entity identified with the `options.data` here
      // call `options.success(entity)` when I'm done
    }
  }

});

It can be as simple as this, but I need a little more than providing an object literal for a transport in my DataSource. I want to encapsulate all of the core options that I need for my Backbone DataSource while still allowing things to be customized. I also need to keep track of the actual Backbone.Collection being used as the backing store for the DataSource. To do all of this, then, I decided to create a custom class called kendo.Backbone.DataStore.

An Outline For kendo.Backbone.DataSource

At a high level, my custom DataSource type largely looks like the transport that I showed above. But instead of creating an object literal for it, I'm creating a new constructor function that can be used to create instances of the transport.

Outline Of BackboneTransport

var BackboneTransport = function(collection){
  this._collection = collection;
};

_.extend(BackboneTransport.prototype, {
  create: function(options){ /* ... */ },

  read: function(options){ /* ... */ },

  update: function(options){ /* ... */ },

  destroy: function(options){ /* ... */ },
});

I'm creating a new constructor function because I need the transport to hold on to the Backbone.Collection reference that I send to it, and I want every instance of my BackboneTransport to have it's own reference to the correct Backbone.Collection.

Note that I'm also taking advantage of JavaScript's prototypal inheritance in adding my CRUD methods to the prototype. To do this, I'm using the underscore.js extend method. This method allows me to easily add methods and data to an existing object using an object literal. In this case, I'm adding my CRUD methods to the BackboneTransport prototype, making these methods available to every instance of BackboneTransport.

As I said before, I want to provide a few default options for my DataSource and not just provide a transport implementation. To do this, I'm creating a kendo.Backbone.DataSource object that extends from kendo.data.DataSource.

A Custom DataSource

kendo.Backbone = kendo.Backbone || {};

kendo.Backbone.DataSource = kendo.data.DataSource.extend({
  init: function(options) {
    var bbtrans = new BackboneTransport(options.collection);
    _.defaults(options, {transport: bbtrans, autoSync: true});

    kendo.data.DataSource.fn.init.call(this, options);
  }
}); 

Here, I'm creatiing a namespace for kendo.Backbone so that I can attach my DataSource object.

I'm then extending from kendo.data.DataSource to create a custom DataSource that inherits directly from the original. This works very similarly to Backbone's extend method, but is provided by Kendo UI's DataSource in this case.

Within the init function (the Kendo UI equivalent of Backbone's initialize function), I'm creating a new instance of my BackboneTransport. I'm using this to set up a few default options and then forwarding the options to the super-type's init function in order to get the DataSource completely up and running.

The use of underscore's defaults method allows me to specify values for my options that will be used if those values have not already been provided in the options object. In other words, I can override these using the constructor for my DataSource:

Override Default Options

new kendo.Backbone.DataSource({
  autoSync: false // override the default that I set
});

Normally, I won't need to override this option, though. In fact, I usually want the autoSync option set to true so that any change made to the DataSource will be pushed to my BackboneTransport instance immediately.

Now that we have the outline for the custom Transport and DataSource covered, let's look at what it takes to fill in the CRUD operations for the transport.

Create

Creating a new model is fairly easy. Backbone.Collection has an add method that we can use for now. There is a create method as well, but this method will push data back to the server. Right now, I want to ignore the server communication that Backbone.Collection provides, and stick with an in-memory collection.

One thing we do need, though, is a "id" field and incrementing value. To keep this simple, we'll use a simple integer value and increment it every time we create a new model.

Create

  create: function(options){
    // increment the id
    if (!this._currentId) { this._currentId = this._collection.length; }
    this._currentId += 1;

    // set the id on the data provided
    var data = options.data;
    data.id = this._currentId;

    // create the model in the collection
    this._collection.add(data);

    // tell the DataSource we're done
    options.success(data);
  }

Read

Reading data is the easiest of all the code we have to add to the custom transport. Backbone.Collection provides a toJSON method that returns the models as an array object object literals, which is what the DataSource wants.

Read

  read: function(options){
    options.success(this._collection.toJSON());
  }

Update

Updating a model is fairly simple as well, but it does need a few steps to get it done. First, we have to find the model that we're trying to update. Since we are handed an object literal as options.data, we need to use the id that we added to the data to find the model. Once we have that, we can update the model as we normally would with Backbone.

Update

  update: function(options){
    // find the model
    var model = this._collection.get(options.data.id);

    // update the model
    model.set(options.data);

    // tell the DataSource we're done
    options.success(options.data);
  }

Destroy

Destroying a model is also fairly simple. As I noted in the "create" function's description, I'm only concerned with an in memory representation of the Backbone.Collection right now. Considering this, I'll avoid using the methods that actually destroy through the Backbone.Collection server communications. Instead, I'll just use the remove method to remove the model from the Collection.

Once again, we have to find the model by id since we are handed an object literal representation of the model.

Destroy

  destroy: function(options){
    // find the model
    var model = this._collection.get(options.data.id);

    // remove the model
    this._collection.remove(model);

    // tell the DataSource we're done
    options.success(options.data);
  }

Putting It All Together: A Demo App

Now that we have our custom transport and DataSource built, it's time to put together a simple demo application. As with my first two blog posts on the DataSource, I'm going to create a very simple "to do" application.

To really illustrate the value of my custom DataSource, though, I'm not going to manually run methods and event handlers as I did previously. Instead, I'm going to stick to the default behaviors that the Kendo UI Grid provides. This will better illustrate how simple it was to provide the functionality that Kendo UI needs, when creating a custom DataSource.

(You can see the full code and the functioning demo app at this JSFiddle.)

As you can see, I didn't get the usability factor set quite right in this sample. I chose to leave the grid alone and not provide any custom usability enhancements, though. I wanted to illustrate how a few methods can be used to provide a completely new data transport for a Kendo UI DataSource, and show that it is possible, and fairly simple, to wrap a DataSource around a Backbone.Collection.

The First Steps Of A Long Journey

The code that I've shown here is far from complete and definitely not ready for production use. But it does illustrate some of the key concepts and shows that it is possible and some-what simple to do. It also paves the way for future work that I will be engaged in.

There is a lot of work left to do, though. Right now this only provides synchronization one-way: from the DataSource to the Backbone.Collection. But what happens when the Backbone.Collection is reset with new data? And what about persisting the data back to a server, through the Collection's "save" method? There's much work to be done, but this is a good first step.

About the Author
is a Developer Advocate for Kendo UI, a developer, speaker, trainer, screen-caster and much more. He's been slinging code since the late 80’s and doing it professionally since the mid 90's. These days, Derick spends his time primarily writing javascript with back-end languages of all types, including Ruby, NodeJS, .NET and more. Derick blogs at DerickBailey.LosTechies.com, produces screencasts at WatchMeCode.net, tweets as @derickbailey and provides support and assistance for JavaScript, BackboneJS, MarionetteJS and much more around the web.

Building a better UI – JSP Wrappers part 2

$
0
0

Last week we dove headfirst into Java, Servlets and the Kendo UI JSP Wrappers. This week we'll expand on that experience and explore the most popular and feature rich control in the Kendo UI arsenal - the venerable Grid.

The Grid widget isn't just an interface for displaying rows of data. No; it's a complete CRUD slinging, filtering, sorting, paging, grouping, aggregating powerhouse. The reality is that many applications are mostly just a Grid laid on top of a database providing a user with a convenient view and a meaningful and safe avenue to control that data.

You should definitely pick up last weeks project (Part 1) before you proceed. If you aren't following along in the code, then at least read through the previous article to get you up to speed since I'm not going to repeat a lot of the plumbing that was laid down last week.

Grab the source

If you just want the completed code from this article, grab the completed project from the GitHub repo.

Lets get started...

The products model

We are going to create a grid that allows editing of the Products table. Before we model this table in our code, lets have a look at what the schema for the table looks like in the database.

northwind-schema

Notice that the Products table is related to both the Categories and Suppliers tables. This means that in order for us to effectively edit a product, we need to be able to edit its supplier and category as well. To do this, we are going to setup models for not just products, but categories and suppliers as well.

Right-click the models and create a new class. Call it Supplier. Create properties for the SupplierID and SupplierName and generate getters and setters. It's also a good idea to create a constructor that takes in these properties as variables so it makes constructing one of these objects slightly less painful.

package models;

public class Supplier {

  private int SupplierID;
  private String SupplierName;

  public int getSupplierID() {
    return SupplierID;
  }
  public void setSupplierID(int supplierID) {
    SupplierID = supplierID;
  }
  public String getSupplierName() {
    return SupplierName;
  }
  public void setSupplierName(String supplierName) {
    SupplierName = supplierName;
  }

  public Supplier() { }

  public Supplier(int supplierID, String supplierName) {
    setSupplierID(supplierID);
    setSupplierName(supplierName);
  }

}

 

Now repeat the same process but this time call the class Category and give it it's respective properties.

package models;

public class Category {

  private int CategoryID;
  private String CategoryName;

  public int getCategoryID() {
    return CategoryID;
  }
  public void setCategoryID(int categoryID) {
    CategoryID = categoryID;
  }
  public String getCategoryName() {
    return CategoryName;
  }
  public void setCategoryName(String categoryName) {
    CategoryName = categoryName;
  }

  public Category() { }

  public Category(int categoryID, String categoryName) {
    setCategoryID(categoryID);
    setCategoryName(categoryName);
  }

}

 

Now we are ready to create the Product model. For it's Category and Supplier, we'll use the model objects we just created. Modeling our data this way matches the database model more closely and stops us from duplicating properties like CategoryID, which appears in both the Category model and the Product model.

Right-click the models package and create a new class called Product. We are not going to be using all of the fields from the database, but just enough to examine some of the finer points of the grid.

package models;

public class Product {

  private int ProductID;
  private String ProductName;
  private models.Supplier Supplier;
  private models.Category Category;
  private float UnitPrice;
  private int UnitsInStock;
  private Boolean Discontinued;

  public int getProductID() {
    return ProductID;
  }
  public void setProductID(int productID) {
    ProductID = productID;
  }
  public String getProductName() {
    return ProductName;
  }
  public void setProductName(String productName) {
    ProductName = productName;
  }
  public float getUnitPrice() {
    return UnitPrice;
  }
  public void setUnitPrice(float unitPrice) {
    UnitPrice = unitPrice;
  }
  public int getUnitsInStock() {
    return UnitsInStock;
  }
  public void setUnitsInStock(int unitsInStock) {
    UnitsInStock = unitsInStock;
  }
  public Boolean getDiscontinued() {
    return Discontinued;
  }
  public void setDiscontinued(Boolean discontinued) {
    Discontinued = discontinued;
  }
  public models.Supplier getSupplier() {
    return Supplier;
  }
  public void setSupplier(models.Supplier supplier) {
    Supplier = supplier;
  }
  public models.Category getCategory() {
    return Category;
  }
  public void setCategory(models.Category category) {
    Category = category;
  }

}

 

Products repository

As we are about to create another repository object, we are going to be duplicating code. This is usually a bad idea. In our scenario, all our repositories have at the very least a Connection object at the top and a constructor where the driver is initalized. At this point, it's a good idea to create a base repository class that our other classes can inherit from.

Right-click the repositories folder and create a new class called Repository. This class will simply hold the connection object at the top and will also take care of initialing the driver in the constructor.

Repository Base Class

package repositories;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class Repository {

  public Connection conn = null;

  public RepositoryBase(String path) {
    // initialize the database driver
        try {
          Class.forName("org.sqlite.JDBC");

      // set the connection instance
      connection = DriverManager.getConnection("jdbc:sqlite:" + path);

    } catch (ClassNotFoundException | SQLException e) {
      e.printStackTrace();
    }
  }

}

 

Now we can alter the EmployeesRepository to inherit from this class which will remove some code.

public class EmployeeRepository extends Repository {

  public EmployeeRepository(String path) {
    super(path);
  }

  ...

 

Right-click the repositories folder and add a new item called ProductsRepository. Change the Superclass so that it uses Repository.

repository_super

Implement a listProducts method which will return all of the products from the database in a collection of Product model objects.

package repositories;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class ProductRepository extends Repository {

  public ProductRepository(String path) {
    super(path);
  }


  public List listProducts() throws SQLException {

    List products = new ArrayList();
    PreparedStatement stmt = null;
    ResultSet rs = null;

    try {

      String sql = "SELECT p.ProductID, p.ProductName, p.SupplierID, s.CompanyName, " +
             "p.CategoryID, c.CategoryName, p.UnitPrice, p.UnitsInStock, p.Discontinued " +
             "FROM Products p " +
             "JOIN Suppliers s ON p.SupplierID = s.SupplierID " +
             "JOIN Categories c ON p.CategoryID = c.CategoryID";

      stmt = super.conn.prepareStatement(sql);

      rs = stmt.executeQuery();

      while(rs.next()) {

        models.Product product = new models.Product();

        product.setProductID(rs.getInt("ProductID"));
        product.setProductName(rs.getString("ProductName"));
        product.setSupplier(new models.Supplier(rs.getInt("SupplierID"), rs.getString("CompanyName")));
        product.setCategory(new models.Category(rs.getInt("CategoryID"), rs.getString("CategoryName")));
        product.setUnitPrice(rs.getFloat("UnitPrice"));
        product.setUnitsInStock(rs.getInt("UnitsInStock"));
        product.setDiscontinued(rs.getBoolean("Discontinued"));

        products.add(product);        
      }
    } 
    finally {
      rs.close();
      stmt.close();
    }

    return products;
  }

}

 

Products servlet

Right-click the api folder and create a new servlet. Call it Products and change the URL mapping to /api/products.

products_servlet

This servlet needs to have a repository instance as well as the overridden init method for supporting our file based database. We can then implement the doGet method to return the simple list of products as JSON.

package api;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import repositories.ProductsRepository;

import com.google.gson.Gson;

/**
 * Servlet implementation class Products
 */
@WebServlet("/api/products")
public class Products extends HttpServlet {
  private static final long serialVersionUID = 1L;

  // employee repository class
  private repositories.ProductsRepository _repository;
  private Gson _gson;

    public Products() {
        super();

        // initialize the Gson library
        _gson = new Gson();
    }

    public void init() throws ServletException {
      super.init();

      // create a new instance of the repository class. pass in the path to the data/sample.db
      // file which we can get by getting the servlet context, then calling 'getRealPath'
      _repository = new ProductsRepository(this.getServletContext().getRealPath("data/sample.db"));
    }

  /**
   * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
   */
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    // set the content type we are sending back as JSON
    response.setContentType("application/json");

    try {

      List<models.Product> products = _repository.listProducts(); 

      // convert the list to json and write it to the response
      response.getWriter().print(_gson.toJson(products));
    }
    catch (Exception e) {
      response.sendError(500);
    }
  }
}

 

Creating the Kendo UI Grid

We are now ready to create a products page with a grid. Add a new JSP page to your project called products.jsp. Remember to include the Kendo UI CSS, JavaScript and jQuery in your project along with the Kendo UI tag library.

<%@taglib prefix="kendo" uri="http://www.kendoui.com/jsp/tags"%>
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
  <title>Insert title here</title>
  <!-- kendo css files -->
  <link href="styles/kendo.common.min.css" rel="stylesheet">
  <link href="styles/kendo.default.min.css" rel="stylesheet">
  <!-- jquery and kendo javascript. jquery MUST be first. -->
  <script src="js/jquery.min.js"></script>
  <script src="js/kendo.all.min.js"></script>
</head>
<body>

  <!-- products grid will go here -->

</body>
</html>

 

Creating the initial grid is really quite easy. Just a kendo:grid tag and a defined read attribute for the transport will do the trick.

<body>

  <kendo:grid name="products">
    <kendo:dataSource>
      <kendo:dataSource-transport read="api/products"></kendo:dataSource-transport>
    </kendo:dataSource>
  </kendo:grid>

</body>

 

grid1

However that's not exactly the way we want the data displayed. All the ID columns are visible and since the Supplier and Category are actually objects, they just show up as [object Object]. We can fix this by defining the columns that we want to display.

<body>

  <kendo:grid name="products">
    <kendo:dataSource>
      <kendo:dataSource-transport read="api/products"></kendo:dataSource-transport>
    </kendo:dataSource>
    <kendo:grid-columns>
      <kendo:grid-column title="Name" field="ProductName" />
      <kendo:grid-column title="Supplier" field="Supplier.SupplierName" />
      <kendo:grid-column title="Category" field="Category.CategoryName" />
      <kendo:grid-column title="Price" field="UnitPrice" />
      <kendo:grid-column title="# In Stock" field="UnitsInStock" />
      <kendo:grid-column title="Discontinued" field="Discontinued" />
    </kendo:grid-columns>
  </kendo:grid>

</body>

 

grid2

Grid paging

This looks a lot better. With a recordset this large though, there are two drawbacks. The first one is that we are returning all of the data at one time when we might only need to see one page. The second is that we have to scroll through an exhaustive list of items. This is hard on the eyes.

The Kendo UI Grid supports two types of paging: client-side and server-side.

Client-side paging

Client-side paging happens when you retrieve all of the records from the server and then page through them in the browser. That eliminates the second of the two problems that we have. To turn this on, you only need to set pageable="true" on the grid and pageSize on the grid DataSource.

<body>

  <kendo:grid name="products" pageable="true">
    <kendo:dataSource pageSize="10">
      <kendo:dataSource-transport read="api/products"></kendo:dataSource-transport>
    </kendo:dataSource>
    <kendo:grid-columns>
      <kendo:grid-column title="Name" field="ProductName" />
      <kendo:grid-column title="Supplier" field="Supplier.SupplierName" />
      <kendo:grid-column title="Category" field="Category.CategoryName" />
      <kendo:grid-column title="Price" field="UnitPrice" />
      <kendo:grid-column title="# In Stock" field="UnitsInStock" />
      <kendo:grid-column title="Discontinued" field="Discontinued" />
    </kendo:grid-columns>
  </kendo:grid>

</body>

 

grid3

Now we have a nice pager control at the bottom with a total count in the right-hand corner. A much better all around UX.

Server-side paging

This works, but we are still slamming all 77 record from the database into browser memory. 77 records probably won't make the browser complain, but imagine if we had 7700. You would definitely not want to send all of those records to your user. That's an abuse of bandwidth, memory and your user's good sensibilities.

For this reason we can turn on server-side paging with the serverPaging="true" setting.

Since this is the beta, we also need to tweak the parameters a bit in the parameterMap as they are sent as JSON by default.

<body>

  <kendo:grid name="products" pageable="true">
    <kendo:dataSource pageSize="10" serverPaging="true">
      <kendo:dataSource-transport read="api/products">
        <kendo:dataSource-transport-parameterMap>
          <script>
            function parameterMap(options, operation) {
              return {
                skip: options.skip,
                take: options.take
              };
            }
          </script>
        </kendo:dataSource-transport-parameterMap>
      </kendo:dataSource-transport>
    </kendo:dataSource>
    <kendo:grid-columns>
      <kendo:grid-column title="Name" field="ProductName" />
      <kendo:grid-column title="Supplier" field="Supplier.SupplierName" />
      <kendo:grid-column title="Category" field="Category.CategoryName" />
      <kendo:grid-column title="Price" field="UnitPrice" />
      <kendo:grid-column title="# In Stock" field="UnitsInStock" />
      <kendo:grid-column title="Discontinued" field="Discontinued" />
    </kendo:grid-columns>
  </kendo:grid>

</body>

 

The current beta stringifies all request parameters as to send them as JSON. The final release will NOT do this, so the parameterMap step will be unnecessary.

Once we do this, the grid will expect us to do 2 things.

  1. Only return the current page of items
  2. ALWAYS return a total count of all of the items in the table

To do this, we are going to create a generic response to send back to the Kendo UI DataSource. It will contain the grand total, as well as a collection of the items in the current page.

Right-click the models package and create a new class called DataSourceResult. Create two properties in that class – Total and Data.

package models;

import java.util.List;

public class DataSourceResult {

  private int Total;
  private List<?> Data;
  public int getTotal() {
    return Total;
  }
  public void setTotal(int total) {
    Total = total;
  }
  public List<?> getData() {
    return Data;
  }
  public void setData(List<?> data) {
    Data = data;
  }

}

 

Notice that we are sending back a list of type ?. That allows us to send back a list of anything so that we can use this DataSourceResult model object over and over again with data of any type. This prevents us from having to create an EmployeesResponse, ProductsResponse and so on and so forth.

We need to modify our ProductsRepository to support paging.

With SQLite3, we can use the LIMIT clause which looks like this: LIMIT skip,take. We tell it how many records to skip, and then how many to return. This gives us the paging we need. We can add the take and skip parameters to the doList method and then apply them to the prepared statement.

public List<models.Product> listProducts(int skip, int take) throws SQLException {

  List<models.Product> products = new ArrayList<models.Product>();
  PreparedStatement stmt = null;
  ResultSet rs = null;

  try {

    String sql = "SELECT p.ProductID, p.ProductName, p.SupplierID, s.CompanyName, " +
           "p.CategoryID, c.CategoryName, p.UnitPrice, p.UnitsInStock, p.Discontinued " +
           "FROM Products p " +
           "JOIN Suppliers s ON p.SupplierID = s.SupplierID " +
           "JOIN Categories c ON p.CategoryID = c.CategoryID " +
           "LIMIT ?,?";

    stmt = super.conn.prepareStatement(sql);

    stmt.setInt(1, skip);
    stmt.setInt(2, take);

    rs = stmt.executeQuery();

    while(rs.next()) {

      models.Product product = new models.Product();

      product.setProductID(rs.getInt("ProductID"));
      product.setProductName(rs.getString("ProductName"));
      product.setSupplier(new models.Supplier(rs.getInt("SupplierID"), rs.getString("CompanyName")));
      product.setCategory(new models.Category(rs.getInt("CategoryID"), rs.getString("CategoryName")));
      product.setUnitPrice(rs.getFloat("UnitPrice"));
      product.setUnitsInStock(rs.getInt("UnitsInStock"));
      product.setDiscontinued(rs.getBoolean("Discontinued"));

      products.add(product);        
    }
  } 
  finally {
    rs.close();
    stmt.close();
  }

  return products;
}

 

The doList method will provide the Data portion of the DataSourceResult, but we need a method to return a total count of the records. Add a method called getCount which will execute a simple COUNT on the products table.

public int getCount() throws SQLException {

  PreparedStatement stmt = null;
  ResultSet rs = null;
  int total = 0;

  try {

    // create a prepared statement
    stmt = super.conn.prepareStatement("SELECT COUNT(*) AS Total FROM Products");

    // execute the statment into the result set
    rs = stmt.executeQuery();

    while(rs.next()) {
      total = rs.getInt("Total");
    }
  }
  finally {
    rs.close();
    stmt.close();
  }

  return total;
}

 

Switch over to the Products servlet.

In the servlet, we can retrieve the skip and take parameters right off of the request by using the getParameter method. We will give them a default value in case no values were sent. Then we just pass them into the ProductsRepository which will apply them to the query.

Instead of serializing a Products model, we are now going to return a DataSourceResult model instead. This means we need to construct one. We use the getCount method for the Total and the doList for the Data.

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  // set the content type we are sending back as JSON
  response.setContentType("application/json");

  // get the take and skip parameters
  int skip = request.getParameter("skip") == null ? 0 : Integer.parseInt(request.getParameter("skip"));
  int take = request.getParameter("take") == null ? 20 : Integer.parseInt(request.getParameter("take"));

  try {

    // create a new DataSourceResult to send back
    models.DataSourceResult result = new models.DataSourceResult();

    // set the total property
    result.setTotal(_repository.getCount());

    // set the data
    result.setData(_repository.listProducts(skip, take)); 

    // convert the DataSourceReslt to JSON and write it to the response
    response.getWriter().print(_gson.toJson(result));
  }
  catch (Exception e) {
    response.sendError(500);
  }
}

 

If you were to try and run the application now, you would see that the grid will display no records at all. This is due to the fact that we completely changed the composition of the JSON response by introducing the DataSourceResult.  The Products data is no longer the top level element. We need to tell the grid where in the JSON it can find the repeating data elements (Data), and which field contains the total number of records (Total).

This is what the DataSource Schema is for. We simply set data="Data" and total="Total".

<body>

  <kendo:grid name="products" pageable="true">
    <kendo:dataSource pageSize="10" serverPaging="true">
      <kendo:dataSource-transport read="api/products">
        <kendo:dataSource-transport-parameterMap>
          <script>
            function parameterMap(options, operation) {
              return {
                skip: options.skip,
                take: options.take
              };
            }
          </script>
        </kendo:dataSource-transport-parameterMap>
      </kendo:dataSource-transport>
      <kendo:dataSource-schema data="Data" total="Total"></kendo:dataSource-schema>
    </kendo:dataSource>
    <kendo:grid-columns>
      <kendo:grid-column title="Name" field="ProductName" />
      <kendo:grid-column title="Supplier" field="Supplier.SupplierName" />
      <kendo:grid-column title="Category" field="Category.CategoryName" />
      <kendo:grid-column title="Price" field="UnitPrice" />
      <kendo:grid-column title="# In Stock" field="UnitsInStock" />
      <kendo:grid-column title="Discontinued" field="Discontinued" />
    </kendo:grid-columns>
  </kendo:grid>

</body>

 

Now the grid paging is being done by the server. That means that we only ever get 10 records at a time. The first ten records are loaded when the grid loads. Any subsequent pages are loading just-in-time as the user pages through the grid. If you have a wait time for records, the grid will automatically display a loader for you to give your users a visual indicator that work is indeed in process.

You can open your browser developer tools (F12) and watch the network traffic. Notice that as you move from page to page, a new request is sent to the server and you get back just the 10 records you requested.

network_traffic_paging

Building a better UI

Building HTML5 applications is not about doing everything in the browser, it's about levering the the server (data operations) and the browser (displaying data) in the areas where they are the strongest.

Delivering data in small chunks to your users as they need it without making a complete round-trip to the server is not only highly efficient, but it also sets you up for success in lower bandwidth scenarios - like mobile devices.

Grab the source

Grab the completed source from today's project on the GitHub repo.

Next week we'll round this out by adding full CRUD to our grid. This will be very interesting since Suppliers and Categories are in different tables, but the user doesn't need to know that. We'll also implement some validation and take a look at some options for creating editing interfaces within the Kendo UI Grid.

About the Author
is a web developer living in Nashville, TN. He enjoys working with and meeting developers who are building mobile apps with jQuery / HTML5 and loves to hack on social API's. Burke works for Telerik as a Developer Evangelist focusing on Kendo UI. Burke is @burkeholland on Twitter.

PHP and Kendo UI

$
0
0

If you've been following me on Twitter you've probably watched a few of my trials and successes in getting PHP up and running on my computer. It's been fun digging in to something that I haven't used in 13+ years. PHP has certainly changed a lot since then, and the community of PHP developers has gone above my expectations in helping me get back in to it.

But more importantly than my own curiosity and love of working with great communities, I'm digging in to PHP this for a reason related to Kendo UI.

A Sneak Peak At Our DataSource Wrappers

The engineering team has been hard at work on building out the PHP wrappers for Kendo UI, and I want to show you a sneak peak of what this is shaping up to be. The gist of it is that they will allow you to use server-side PHP code to generate your client-side JavaScript for Kendo UI, much the same way that our ASP.NET MVC and JSP (beta) wrappers work.

Here, for example, is a snippet of what it will look like to create a kendo.data.DataSource using our new PHP wrappers:

DataSource Example

$model
  ->id('ProductID')
  ->addField(
    $productIDField, 
    $productNameField, 
    $unitPriceField, 
    $unitsInStockField, 
    $discontinuedField
  );

$schema = new \Kendo\Data\DataSourceSchema();
$schema
  ->data('data')
  ->errors('errors')
  ->model($model)
  ->total('total');

$dataSource = new \Kendo\Data\DataSource();

$dataSource->transport($transport)
  ->batch(true)
  ->pageSize(30)
  ->schema($schema);

This, along with a few details I’ve left out, and combined with the Kendo UI Grid wrapper, will produce the following JavaScript in your page:

PHP Wrapper Output

jQuery("#grid").kendoGrid({
  "columns": [
    // ...
  ],
  "dataSource": {
    "transport": {
      // ...
    },
    "batch": true,
    "pageSize": 30,
    "schema": {
      "data": "data",
      "errors": "errors",
      "model": {
        "id": "ProductID",
        "fields": [ /* ... */ ]
      },
      "total": "total"
    }
  },
  "toolbar": [
    { "name": "create" }, 
    { "name": "save" }, 
    { "name": "cancel" }
  ],
  "height": 400,
  "navigatable": true,
  "editable": true,
  "pageable": true
});

I've omitted a few of the lengthier details here, to keep the code more readable. The result, though, is a fully functioning Kendo UI Grid. It's the same grid we all know and love, still running in the browser. The difference is that we're building the resulting JavaScript with server side PHP.

More PHP, Please!

Now I know the DataSource may not be the most exciting piece of our framework, but it is a critical one. Nearly every Kendo UI widget and control can take advantage of the DataSource to provide 2-way binding in and out of a backing data store. This makes it a foundation for many other PHP wrappers that we will be providing.

Ultimately, this is just a quick preview of what's to come, using an early version of what has been built. There's a good chance that the details I'm showing here will change before the final release, so don't take this blog post as the gospel of what will be. There is also a lot more to the PHP side of things than I'm able to show at this time, and a lot more to come!

If you want to know more, if you want to see the suite of PHP wrappers in action and possibly get a discount on them when they are released, let us know! Send an email to Sasha Krsmanovic and let him know that you want to be in on the release webinar for the PHP wrappers. We'll get you added to the list so that you can stay informed.

About the Author
is a Developer Advocate for Kendo UI, a developer, speaker, trainer, screen-caster and much more. He's been slinging code since the late 80’s and doing it professionally since the mid 90's. These days, Derick spends his time primarily writing javascript with back-end languages of all types, including Ruby, NodeJS, .NET and more. Derick blogs at DerickBailey.LosTechies.com, produces screencasts at WatchMeCode.net, tweets as @derickbailey and provides support and assistance for JavaScript, BackboneJS, MarionetteJS and much more around the web.

Custom editing in JSP with the Kendo UI Grid

$
0
0

In the last two weeks we have gone from zero to HTML5 hero with the Kendo UI JSP Wrappers. That was corny I know, but lets take a look at what we did in each of the previous two parts...

Part 1 (Read It)

  • Created a basic JSP Dynamic Web Application
  • Added a data access layer
  • Created the first servlet
  • Used the Kendo UI TreeView to lazy load hierarchical data

Part 2 (Read It)

  • Expanded the data access layer to include products with relationships to the categories and suppliers tables
  • Wired up a Kendo UI Grid to the products table
  • Implemented server side paging for wicked fast presentation of large data sets

In last week's post, we also talked about how a great many applications are not much more than a grid laid on top of a relational database. The grid needs to expose an editing surface for users where they can safely manage their own data in an interface that stitches back together the data that has been broken apart for storage.

Editing In A Kendo UI Grid

Today we will look at enabling more editing features in the Kendo UI grid with the JSP wrappers.

You can grab the code for today's article from the GitHub Repo (It’s part 3).

To turn editing on in the grid, it's pretty trivial. You just need to set the editable: true attribute on the grid.

<kendo:grid name="products" pageable="true" editable="true">
  <kendo:dataSource pageSize="5" serverPaging="true">
    <kendo:dataSource-transport read="api/products">
      <kendo:dataSource-transport-parameterMap>
      .....

 

What you will notice when you do that is that when you click on an item in the grid, it becomes editable. What's even better is that Kendo UI is smart enough to recognize that you have numeric data types so it gives you a numeric textbox. It also automatically gives you a checkbox for the boolean field.

gridedit1

However, the Supplier and Category are plain text fields. We don't want this. We want them to be able to choose from a list of already predefined values for categories and suppliers in the database. Also, the grid is giving us a numeric textbox for the price, but we don't want a plain number there, we want currency.

Formatting grid columns

We can solve the price issue right away by specifying a format for the column. In this case, we want currency so all we need to do is specify a "c". Format are specified as {0:format}.

<kendo:grid-columns>
  <kendo:grid-column title="Name" field="ProductName" />
  <kendo:grid-column title="Supplier" field="Supplier.SupplierName" />
  <kendo:grid-column title="Category" field="Category.CategoryName" />
  <kendo:grid-column title="Price" field="UnitPrice" format="{0:c}" />
  <kendo:grid-column title="# In Stock" field="UnitsInStock" />
  <kendo:grid-column title="Discontinued" field="Discontinued" />
</kendo:grid-columns>

 

We could specify a more restrictive format for the column You can refer to the Kendo UI formatting docs for all the available options and formats.

The supplier and category fields seem fine right now, but when we go into edit mode, they are displayed as plain text. We need dropdowns instead. We are currently bound to the SupplierName and CategoryName fields. Instead, we are going to bind to the Supplier and Category objects themselves. Since we are now bound to an object instead of a value, we need to specify a template so the grid doesn't give us [object Object].

<kendo:grid-columns>
  <kendo:grid-column title="Name" field="ProductName" />
  <kendo:grid-column title="Supplier" field="Supplier" template="#: Supplier.SupplierName #" />
  <kendo:grid-column title="Category" field="Category" template="#: Category.CategoryName #" />
  <kendo:grid-column title="Price" field="UnitPrice" format="{0:c}" />
  <kendo:grid-column title="# In Stock" field="UnitsInStock" />
  <kendo:grid-column title="Discontinued" field="Discontinued" />
</kendo:grid-columns>

 

Kendo UI Templates are a way to specify how the data is output. You use #: on the left, put your binding expression in the middle, then close it off with another #. The binding expression is the specific field in the source object you want the value from.

We could mix HTML in with this as well if we wanted to. For instance, if we wanted to display a checkmark in the Discontinued column instead of just true/false, we could give the grid a template.

<kendo:grid-columns>
  <kendo:grid-column title="Name" field="ProductName" />
  <kendo:grid-column title="Supplier" field="Supplier" template="#: Supplier.SupplierName #" />
  <kendo:grid-column title="Category" field="Category" template="#: Category.CategoryName #" />
  <kendo:grid-column title="Price" field="UnitPrice" format="{0:c}" />
  <kendo:grid-column title="# In Stock" field="UnitsInStock" />
  <kendo:grid-column title="Discontinued" field="Discontinued" 
             template="
             # if (data.Discontinued) { # 
               <span class='k-icon k-i-tick'></span> 
             # } #" />
</kendo:grid-columns>

 

gridedit2

These template can execute JavaScript logic like I have included above. To identify JavaScript blocks, you open with a # and the close with a # before you return to straight HTML. Refer to the Kendo UI Template documentation for more information on how you can use templates to get complete control over your UI.

Now that we have the Products and Categories templates working, we need to address their edit mode. Right now when the grid goes into edit mode, it will display [object Object] again for Supplier and Category. We need to specify Custom Editors for these fields.

Drop down below the grid and open a script tag. Inside of it create a function called supplierEditor that takes in a container parameter and an options parameter.

.....
</kendo:grid-columns>
  </kendo:grid>

  <script>

    function supplierEditor(container, options) {
      $("<input data-text-field='SupplierName' data-value-field='SupplierID' data-bind='value:" + options.field + "' />")
      .appendTo(container)
      .kendoDropDownList({
        dataSource: {
          transport: {
            read: "api/suppliers"
          }
        }
      });
    };

  </script>

 

This function does a few things so lets break it down.

  1. Creates a new input element with jQuery. This new element has all of it's configuration in the HTML by way of data attributes. Any setting on a Kendo UI Widget can be declared in the HTML by using a data attribute. Any camel cased settings are separated by dashes (i.e. dataTextField becomes data-text-field)
  2. The new input is appended to the container which is the grid row
  3. The input is transformed into a Kendo UI DropDown List and reads from the api/suppliers endpoint which doesn't exist yet.

Since the Category field is nearly identical to the Supplier, just copy the function and change any references of Supplier to Category.

function categoryEditor(container, options) {
  $("<input data-text-field='CategoryName' data-value-field='CategoryID' data-bind='value:" + options.field + "' />")
  .appendTo(container)
  .kendoDropDownList({
    dataSource: {
      transport: {
        read: "api/categories"
      }
    }
  });
};

 

Lets now define the two endpoints that we need for the DropDown Lists. This would be the api/suppliers and api/categories.

Creating the necessary repositories

If you have been following this series, you know that we will need to create data access endpoints for our Suppliers and Categories.

Right-click the repositories package and create a new class called SuppliersRepository. Make sure you change the Superclass to our Repository base class.

supplierrespository

Create a doList method that returns a list of all the Suppliers from the database.

Remember that I am passing the path string in for my file based database. You will most likely not have to do this so you wouldn't need the constructor like I have it.

package repositories;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class SuppliersRepository extends Repository {

  public SuppliersRepository(String path) {
    super(path);
  }

  public List listSuppliers() throws SQLException {

    PreparedStatement stmt = null;
    ResultSet rs = null;

    // prepare a list of suppliers to populate as a return value
    List suppliers = new ArrayList();

    try {

      // set sql statement
      String sql = "SELECT SupplierID, CompanyName AS SupplierName FROM Suppliers";

      // prepare the string for safe execution
      stmt = super.conn.prepareStatement(sql);

      // execute the statement into a ResultSet
      rs = stmt.executeQuery();

      // loop through the results
      while(rs.next()) {

        // create a new supplier object
        models.Supplier supplier = new models.Supplier();

        // populate it with the values from the database
        supplier.setSupplierID(rs.getInt("SupplierID"));
        supplier.setSupplierName(rs.getString("SupplierName"));

        // add the supplier to the return list
        suppliers.add(supplier);
      }
    }
    finally {
      // close out all connection related instances
      stmt.close();
      rs.close();
    }

    // return the list of suppliers
    return suppliers;
  }
}

 

Now create a repository for the Categories. It's nearly identical to the Suppliers repository with a few subtle differences.

package repositories;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class CategoriesRepository extends Repository {

  public CategoriesRepository(String path) {
    super(path);
  }

  public List<models.Category> listCategories() throws SQLException {

    PreparedStatement stmt = null;
    ResultSet rs = null;

    // create a list of categories to return
    List<models.Category> categories = new ArrayList<models.Category>();

    try {

      // create the sql string
      String sql = "SELECT CategoryID, CategoryName FROM Categories";

      // prepare the string for safe execution
      stmt = super.conn.prepareStatement(sql);

      // execute the sql and return the results to the ResultSet
      rs = stmt.executeQuery();

      // iterate through the result set
      while(rs.next()) {

        // create a new category object
        models.Category category = new models.Category();

        // populate it's values from the database
        category.setCategoryID(rs.getInt("CategoryID"));
        category.setCategoryName(rs.getString("CategoryName"));

        // add it to the list of categories
        categories.add(category);
      }
    }
    finally {
      // close out all connection related instances
      stmt.close();
      rs.close();
    }

    // return the list of categories
    return categories;

  }
}

 

Adding the servlets

By now you should be comfortable creating new servlets. Right-click the api package and select New/Servlet. Call it Suppliers and change the url mapping to /api/suppliers.

suppliersservlet

Include a reference to the SupplierRepository at the top. Also include the Gson library for returning JSON. Again, if you have been following along, this should all be old hat. Remember that I do some server path magic in my samples because I am using a file based database. In the doGet method, return the list of suppliers in JSON format.

package api;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.Gson;

import repositories.SuppliersRepository;

/**
 * Servlet implementation class Suppliers
 */
@WebServlet("/api/suppliers")
public class Suppliers extends HttpServlet {
  private static final long serialVersionUID = 1L;
    private repositories.SuppliersRepository _repository;   
    private Gson _gson = new Gson();

    /**
     * @see HttpServlet#HttpServlet()
     */
    public Suppliers() {
        super();
        // TODO Auto-generated constructor stub
    }

    public void init() throws ServletException {
      super.init();
      _repository = new SuppliersRepository(this.getServletContext().getRealPath("data/sample.db"));
    }

  /**
   * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
   */
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    // create a list of suppliers to send back as JSON
    List<models.Supplier> suppliers = new ArrayList<models.Supplier>();

    try {

      // get the suppliers from the database
      suppliers = _repository.listSuppliers();

      // set the content type we are sending back as JSON
      response.setContentType("application/json"); 

      // print the content to the response
      response.getWriter().print(_gson.toJson(suppliers));

    } catch (SQLException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      response.sendError(500);
    }

  }

}
 

Now we need a Categories servlet.

package api;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import repositories.CategoriesRepository;

import com.google.gson.Gson;

/**
 * Servlet implementation class Categories
 */
@WebServlet("/api/categories")
public class Categories extends HttpServlet {
  private static final long serialVersionUID = 1L;
    private repositories.CategoriesRepository _repository = null;
    private Gson _gson = new Gson();

    /**
     * @see HttpServlet#HttpServlet()
     */
    public Categories() {
        super();
    }

    public void init() throws ServletException {
      super.init();
      _repository = new CategoriesRepository(this.getServletContext().getRealPath("data/sample.db"));
    }

  /**
   * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
   */
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    List<models.Category> categories = new ArrayList<models.Category>();

    try {

      categories = _repository.listCategories();

      // set the content type we are sending back as JSON
      response.setContentType("application/json"); 

      // print the content to the response
      response.getWriter().print(_gson.toJson(categories));

    } catch (SQLException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      response.sendError(500);
    }

  }

}

 

With all that plumbing work out of the way, we just need to have the grid use these new custom editors. We do that by specifying the editor attribute for the Supplier and Category columns.

<kendo:grid-columns>
  <kendo:grid-column field="ProductName" title="Product"></kendo:grid-column>
  <kendo:grid-column field="Supplier" title="Supplier" editor="supplierEditor" template="#: Supplier.SupplierName #"></kendo:grid-column>
  <kendo:grid-column field="Category" title="Category" width="150px" editor="categoryEditor" template="#: Category.CategoryName #"></kendo:grid-column>
  <kendo:grid-column field="UnitPrice" title="Price" format="{0:c}" width="75px"></kendo:grid-column>
  <kendo:grid-column field="UnitsInStock" title="# In Stock" width="80px"></kendo:grid-column>
  <kendo:grid-column field="Discontinued" title="Discontinued" width="100px"></kendo:grid-column>
  <kendo:grid-column>
    <kendo:grid-column-command>
      <kendo:grid-column-commandItem name="edit"></kendo:grid-column-commandItem>
      <kendo:grid-column-commandItem name="destroy"></kendo:grid-column-commandItem>
    </kendo:grid-column-command>
  </kendo:grid-column>
</kendo:grid-columns>

 

I also adjusted the column widths a bit to make things look a tad cleaner. Edit mode now displays a drop down for the Suppliers and Categories.

Screen Shot 2013-02-18 at 4.06.04 PM

We have successfully joined all of the backend data into a cohesive interface for our users.  However lets tweak the edit interface just a bit before we actually wire everything up.

Altering the default edit mode

We can edit a row just by clicking into it, but then each cell is in edit mode individually. I want the whole row in edit mode. To do this, we need to set the grid edit mode to "inline".

<kendo:grid name="products" pageable="true">
  <kendo:grid-editable mode="inline" />
  ....

 

Now clicking on a row will do nothing. We need to add a button which will put the row into edit mode. We do this with Command Columns. Let's also add a delete button while we're at it.  Delete is referred to as Destroy throughout Kendo UI as delete is a JavaScript reserved word.

....
<kendo:grid-column field="UnitsInStock" title="# In Stock" width="80px"></kendo:grid-column>
  <kendo:grid-column title="Discontinued" field="Discontinued" width="100px"
             template="# if (data.Discontinued) { # <span class='k-icon k-i-tick'></span> # } #" />
  <kendo:grid-column>
    <kendo:grid-column-command>
      <kendo:grid-column-commandItem name="edit"></kendo:grid-column-commandItem>
      <kendo:grid-column-commandItem name="destroy"></kendo:grid-column-commandItem>
    </kendo:grid-column-command>
  </kendo:grid-column>
</kendo:grid-columns>

 

Now we get a column with Edit and Delete buttons.

grid-edit-delete

Now clicking a row will put it in update mode. When the grid switches to update mode, the Edit and Delete buttons will turn to Update and Cancel.

editlooksbad

I don't like the way this looks though. The numeric editors are cutting off their content and the buttons stack on each other inside of the column. We can get into adjusting column widths here, but that's tedious. Another option is to just display a popup window for the user to edit in. The grid allows us to do this by simply specifying popup as the edit mode.

<kendo:grid name="products" pageable="true">
  <kendo:grid-editable mode="popup" />

 

grid-edit-pretty

And just like that you get a nice modal window with all the fields aligned, all the labels aligned right and plenty of room for editing.

Grab the source

As always, you can grab the source for this project from the GitHub repo. This is part 3 by the way.

Coming up next

Next we'll look at wiring all this up to the server. We also need to add the ability to create items which is going to take us into DataSource Model territory where we will also be able to leverage the Kendo UI validation framework for easy field validation.

About the Author
is a web developer living in Nashville, TN. He enjoys working with and meeting developers who are building mobile apps with jQuery / HTML5 and loves to hack on social API's. Burke works for Telerik as a Developer Evangelist focusing on Kendo UI. Burke is @burkeholland on Twitter.

Viewing all 181 articles
Browse latest View live