Telerik blogs

When we last looked at the Kendo UI Data Source, we discovered that it could help simplify the process of talking to OData web service end points. By simply setting the "type" property to "odata," the Kendo UI data source automatically handles the nuances of talking to the Netflix OData API. By example:

var data = new kendo.data.DataSource({
    type: "odata",
    transport: {
        read: "http://odata.netflix.com/v2/Catalog/Genres"
    }
});

When the call to the OData endpoint executes, the Kendo UI Data Source automatically produces this RESTful URL:

http://odata.netflix.com/v2/Catalog/Genres?$format=json&$inlinecount=allpages&$callback=callback

How did the Kendo UI Data Source know what to do?

As it turns-out, the Kendo UI Data Source has an interesting extension point that makes it possible to "teach" the data source about any web service.

kendo.data.odata.js

If you look at the Kendo UI source, you discover (among many others) these two files: kendo.data.js and kendo.data.odata.js. The first file provides the majority of the Data Source implementation (as you would expect). The second file is interesting. It extends the kendo.data object to essentially define a default configuration for "odata" web services.

A snippet from this file reveals:

$.extend(true, kendo.data, {
        schemas: {
            odata: {
                type: "json",
                data: "d.results",
                total: "d.__count"
            }
        },
        transports: {
            odata: {
                read: {
                    cache: true, // to prevent jQuery from adding cache buster
                    dataType: "jsonp",
                    jsonpCallback: "callback", //required by OData
                    jsonp: false // to prevent jQuery from adding the jsonpCallback in the query string - we will add it ourselves
                },
...

As you can see, the file is properly configuring the known schema and transport settings for OData. By embedding this configuration information in this adapter, using the Data Source for OData requires much less manual configuration (see snippet #1).

Okay. Makes sense. Can we extend this model to do some interesting things?

Supporting OData CORS with a New Adapter

CORS is an emerging alternative for cross-origin async requests that replaces the need for JSONP hacks. As we demonstrated in a different blog post, when a server is configured with the CORS headers, you can easily use jQuery to query a web service on a different domain using Ajax.

The default Kendo UI OData adapter does not currently support CORS, though. It defaults to the more broadly supported JSONP configuration. Let's create a new Kendo UI Data Source adapter for OData with CORS support:

kendo.data.odataCors.js

(function($) {
    var kendo = window.kendo;

    $.extend(true, kendo.data, {
        schemas: {
            odataCors: {
                type: "json",
                data: "d.results",
                total: "d.__count"
            }
        },
        transports: {
            odataCors: {
                read: {
                    cache: true, // to prevent jQuery from adding cache buster
                    dataType: "json",
                },
                dialect: function(options) {
                    var result = ["$inlinecount=allpages"],
                        data = options || {};

                    //...detail omitted..

                    return result.join("&");
                }
            }
        }
    });
})(jQuery);

I've omitted some of the detail since it is generally the same as the original OData adapter. The most important things to note are the highlighted lines (6 and 13) where I set the new adapter name to "odataCors." This is the name I'll use when configuring the Kendo UI Data Source. Also notice that I've removed all JSONP configuration, clearly making this a CORS-specific OData fetch implementation.

To consume this new adapter in my Data Source and query a CORS-enabled OData service with Kendo UI, I simply change the "type" configuration:

var data = new kendo.data.DataSource({
    type: "odataCors",
    transport: {
        read: "http://odata.netflix.com/v2/Catalog/Genres"
    }
});

Pretty cool! Without changing anything other than the "type" name, I've altered how my data access is working. And really, I could have changed the original OData adapter, making the change completely transparent to my front-end code.

Let's try something a little different.

Abstracting the Twitter Search API

The Kendo UI Data Source works with much more than OData, of course. Let's use this same technique to build an adapter for the public Twitter Search API:

kendo.data.twitterSearch.js

(function ($) {
    var kendo = window.kendo;


    $.extend(true, kendo.data, {
        schemas: {
            twitterSearch: {
                type: "json",
                data: "results",
                total: "results_per_page"
            }
        },
        transports: {
            twitterSearch: {
                read: {
                    url: "http://search.twitter.com/search.json",
                    cache: true, // to prevent jQuery from adding cache buster
                    dataType: "jsonp",
                    jsonp: true
                },
                dialect: function (options) {
                    var result = ["callback=?"],
                        data = options || {};

                    if ("q" in data)
                        result.push("q=" + data.q);

                    if ("pageSize" in data)
                        result.push("count=" + data.pageSize)

                    return result.join("&");
                }
            }
        }
    });
})(jQuery);

As before, we've set our new adapter name ("twitterSearch") and configured the default behaviors for querying Twitter. Most interesting, we've set the Twitter Search URL endpoint and we've defined how a RESTful Twitter query should be constructed using "dialect" (mapping pageSize to the Twitter expected "count" parameter, and "q" for our query value).

Consuming this in the Kendo UI Data Source now only requires that we set a page size and define a query:

var ds = new kendo.data.DataSource({
    type: "twitterSearch",
    serverPaging: true,
    pageSize: 10,
    transport: {
        read: {
            data: { q: "Apple" }
        }
    }
});

You can check-out a live JSBin of this demo to see it in action.

Here's why this is cool:

  1. If the Twitter API search endpoint URL changes, I can simply update my adapter and all Kendo UI Data Sources using that adapter will be ready to go (imagine the benefit if you're working with an API that has a "sandbox" API endpoint and a production API endpoint)
  2. If the parameters for the Twitter API search change, that too is abstracted behind my adapter. A simple update to the adapter Dialect, and all Kendo UI data sources continue to work without any change.

Going Further

Now that you understand how these "adapters" work and why they are cool, you can start to imagine how this approach could be useful in your own development:

  • If you have an enterprise API, create an adapter that can be used to uniformly transition an app to point at DEV, TEST, and PROD endpoints
  • If you need to target multiple versions of an API that have different API parameter names, use an adapter to "hide" the differences from your front-end code.
  • Or, if you simply want to make it easier to work with a public API (like I have done with Twitter), create a simple adapter to keep your JavaScript DRY

There is also the potential for using this approach to do some high-level "mocking" of service data, where you have something like a "kendo.data.mockTwitterSearch.js" and "kendo.data.twitterSearch.js." When you reference the "mock" version of the adapter, it could be configured to return a fixed set of data instead of querying a remote service. Not as powerful or low-level as something like Mockjax, but another interesting application.

This is just one of the many cool features you can use in the Kendo UI Data Source. Enjoy the tip and stay tuned for more Kendo UI highlights.


ToddAnglin_164
About the Author

Todd Anglin

Todd Anglin is Vice President of Product at Progress. Todd is responsible for leading the teams at Progress focused on NativeScript, a modern cross-platform solution for building native mobile apps with JavaScript. Todd is an author and frequent speaker on web and mobile app development. Follow Todd @toddanglin for his latest writings and industry insights.

Comments

Comments are disabled in preview mode.