UI controls for ASP.NET AJAX, MVC, WPF,Silverlight, Windows 8 and Windows Phone
Cross-platform Mobile Development Toolwith cloud-based architecture
Everything you need to build sites andmobile apps with JavaScript and HTML5
One easy tool for Functional, Performance,Load and Mobile software testing
Everything for your online business - contentmanagement, ecommerce, emarketing
Simple and intuitive project managementand collaboration software
Hot on the heels of my post on creating a Kendo UI DataSource for a Backbone.Collection, Brandon Satrom and I built a quick hack to see what it would take to integrate Breeze.js with our DataSource.
If you're not familiar with Breeze.js, it describes itself by saying "Rich data for JavaScript apps is a Breeze". Basically, it's a framework to abstract away all of the complexities of calling data APIs through JavaScript. It takes the complexity of understanding not only XHR objects away, like jQuery's ajax method, but also understanding data formats, query parameter formats, integration with specific back-end data sources, and much more.
Like the Backbone.Collection integration, I'm going to build a custom data transport for Breeze.js. Instead of focusing on the basic CRUD features, though, I want to show some of the more advanced features of reading data: sorting and paging.
To get started, I need a DataSource wrapper that allows me to pass in the needed Breeze.js objects. The end result will be a custom transport that calls through to the Breeze objects through four basic methods: create, read, update, and delete. If you need further review or information on this, be sure to read my previous post on building a DataSource for a Backbone.Collection, and check out the documentation for DataSource transport.
create
read
update
delete
(function ($, kendo, breeze) { 'use strict'; // namespace for the extensions kendo.data.extensions = kendo.data.extensions || {}; // constructor function for the custom transport function BreezeTransport(entityManager, endpoint){ this.entityManager = entityManager; this.endPoint = endPoint; } // instance methods for the custom transport $.extend(BreezeTransport.prototype, { read: function (options) { // ... }, create: function (options) { // ... }, update: function (options) { // ... }, destroy: function (options) { // ... } }); // Create the custom DataSource by extending a kendo.data.DataSource // and specify an init method that wires up needed functionality. kendo.data.extensions.BreezeDataSource = kendo.data.DataSource.extend({ init: function (options) { // build the transport and final options objects var breezeTransport = new BreezeTransport(options.entityManager, options.endpoint); options = $.extend({}, { transport: breezeTransport }, options) // Call the "base" DataSource init function and provide our custom transport object kendo.data.DataSource.fn.init.call(this, options); } }); })($, kendo, breeze);
The Breeze EntityManager is the core of what we work with, and is where an app will configure it's back-end specifics. Passing this in to our custom DataSource will allow all of Breeze's flexibility, but still give us the ability to query the data based on the parameters that the Kendo UI DataSource provides to the read method.
Once we have the custom DataSource and transport outlined, we can focus on read data through the EntityQuery object.
This object allows us to form the query that will be sent back to the API, and includes methods three methods that we'll use for sorting and paging:
These are common method and attribute names used for sorting and paging. The orderBy method is fairly self-explanatory in name. It allows us to sort the results by the configuration we specify. The skip and take methods, in combination, allow us to facilitate paging. These two methods might not be as obvious, though.
orderBy
skip
take
When building a paged data source of any kind, paging can be facilitated with two simple parameters: skip and take. Any system that needs to use paging, then, can provide these parameters and end up with the data set that they expect. When a DataGrid or other page-enabled control says it wants page 2 of a data set, and each page is 10 records long, this gets translated in to the skip and take settings.
Given a page length of 10 and a request for page 2, we can do simple math to get the starting point of the data we want to show from the overall result set. Since we want page 2 of data and the page length is 10, we know that we need to skip the first 10 records... the first page of data. This can be calculated with a little bit of code, quite easily:
var requestedPageLength = 10; var requestedPageNumber = 2; var skip = requestedPageLength * (requestedPageNumber-1); // => 10
We're subtracting 1 from the page number so that we can get the starting point of the second page of data, not the end point.
Even when we request page #1, then we don't want to skip anything - we want to start at the beginning of the record set. The formula still works for this scenario: 10 * (1-1) = 0, which says "skip zero records" or "start at the very beginning."
10 * (1-1) = 0
The take parameter of the skip/take combination is the actual number of records that we want to get - the reuested page length. This parameter tells the data source how many records to return, after it has skipped the number that we said to skip.
var take = requestedPageLength;
The end result of the skip / take combination is a paging system that can be used by nearly any data source and page-enabled control or other code.
Integration between Breeze and the transport read method is easy from here. The options parameter that is passed through the method contains three attributes: sort, skip, and take. We can apply these to the Breeze EntityQuery to get the paged and sorted data that has been requested.
options
sort
read: function(options){ var orderVal = "", sortOps = options.data.sort; // build sorting the way breeze understands it if (sortOps) { orderVal = sortOps.field + " " + sortOps.dir; } // build a query to get the sorted and paged results var query = new breeze.EntityQuery(endpoint) .orderBy(options.data.sort) .skip(options.data.skip) .take(options.data.take); // ... }
Note that we're not just grabbing the raw sort value from the options.data.sort. Instead, we are taking the field and dir attributes from this and creating an orderBy configuration that Breeze understands.
options.data.sort
field
dir
Once we have those values applied, we can execute the query using the EntityManager that was supplied to the DataSource.
read: function(options){ // ... breezeEntityManager.executeQuery(query).then(function(xhr){ options.success(xhr.results); }); }
Executing a Breeze Query will result in a q.js Promise object, which can be chained in to a then callback that is called when the data finally returns from the server. Once the data has returned, we call options.success and pass the resulting data.
then
options.success
With the read-only BreezeDataSource set up, we can use it as we would any other DataSource for a Kendo UI control. We only need to configure a Breeze EntityManager to work with our API, and then pass that to our BreezeDataSource.
// create a Breeze EntityManager and configure // it for the API that we are connecting to var manager = new breeze.EntityManager({ dataService: new breeze.DataService({ serviceName: 'data/', hasServerMetadata: false }) }); // create a BreezeDataSource var ds = new kendo.data.extensions.BreezeDataSource({ entityManager: manager, endpoint: "products.json", pageSize: 10, serverPaging: true, serverSorting: true }); // assign the DataSource to the grid $("#grid").kendoGrid({ columns: [ { field: 'ProductName', title: 'Name'}, { field: 'Supplier.SupplierName', title: 'Supplier'}, { field: 'Category.CategoryName', title: 'Category' } ], dataSource: ds, pageable: true, sortable: true });
This will produce a Kendo UI Grid with the data being read through Breeze.js
About the AuthorDerick Bailey 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.
Hi Derick,
It is great to see Telerik's first post on integration between Kendo UI and Breeze.js. I hope we can see more complex scenarios soon as that is the whole reason for asking for the integration ;-). For simple scenarios we can use Kendo UI’s kendo.data.DataSource and kendo.data.Model classes. For complex scenarios we need Breeze.js.
I fear though that trying to integrate Kendo UI and Breeze.js at the level of kendo.data.DataSource transport operations will not enable you to maximize the benefits of using Breeze.js. At the transport level you are dealing with JSON data objects. The read transport operation will pass JSON data to the DataSource and it is the DataSource that will create instances of the kendo.data.Model class you defined. The data that the transport create, update and destroy operations get passed in is not those kendo.data.Model instances, but JSON data again, and thus contains no change tracking information on whether an instance is dirty or new.
I would like to see Kendo UI and Breeze.js integration at the level of entities, not JSON data. And I suspect this does not involve the kendo.data.DataSource or kendo.data.Model classes.
Kendo’s DataSource can only deal with entities of a single type, does not automatically fixup navigation properties, and its batch support cannot submit changes to instances of different types / multiple DataSources in a single changeset / transaction / unit of work let alone submit the create, update and destroy operations of a single DataSource in a single changeset. It does not act properly as a client cache as a read operation will replace any existing data instead of merging in data.
Breeze.js EntityManager can deal with entities of multiple types, automatically fixing up their navigation properties, can merge data into its client cache, track changes and submit changes in a single unit of work.
Breeze.js will also hold all the metadata regarding entity types: how it creates new entities, data properties, key properties, navigation properties, default property values, validation rules, concurrency tokens, etc. Why would you want to duplicate any of this metadata by defining a kendo.data.Model class? I believe through an adapter the Breeze.js EntityManager can make all entities instances of kendo.data.ObservableObject, like it creates knockout observables by default, but surely you don’t need to go through the pain of duplicating the metadata by defining kendo.data.Model classes. I do think Breeze.js needs a tool to generate TypeScript class representations of all entities and their key, data and navigation properties.
I think the true integration between Kendo UI and Breeze.js would be achieved by making the Kendo UI widgets interact directly with Breeze.js entities and make those widgets understand the change tracking information held in the entity’s EntityAspect and the metadata held in the EntityType. I don’t see a role for the kendo.data.DataSource or kendo.data.Model classes here. For a kendo.ui.Grid to create a new instance it will have ask the view model and the view model will have to ask the Breeze.js EntityManager to create a new instance. When you have a kendo.data.ObservableArray then you can decide in your view model when an entity is removed from the kendo.data.ObservableArray whether you want it removed just from the kendo.data.ObservableArray or whether you also want to flag it for deletion in the EntityManager. When a kendo.ui.Grid column needs to know the type of data (string, number, date) in the column and whether it is editable or not, then I think that needs to be declared on the column, but does not need to be defined in a kendo.data.Model class.
Of course, this kind of deep integration requires a lot of work and probably goes against Telerik’s design principle in Kendo UI of not taking any dependencies on other libraries. However, I don’t see Telerik matching Breeze.js functionality in their own Kendo UI framework anytime soon either. So what can we expect from Kendo UI and Breeze.js integration in the short and long term?
Regards,
Remco
RadEditor - please enable JavaScript to use the rich text editor.
Copyright © 2011 - 2013 Telerik Inc. All rights reserved.