If you haven't heard, Odata is a Web protocol that exposes your data to the Web, allowing consumers to make queries through a set of URI parameters. RadGrid, Telerik's ASP.NET AJAX Grid control features a rich client-side API that enables you to easily bind to OData services and have all the paging, sorting and filtering done without ever posting the page to the server. That is all fine if your data is flat. Binding to hierarchical data, however, is another story. RadGrid officially supports hierarchical databinding on the server only. But let's go unofficial for a while. In this blog post we'll demonstrate an approach for detail table databinding to an OData service right in the client. The example uses 3 levels if hierarchical data, but the approach is general enough to work with an arbitrary number of detail tables.

The Data

How would we go about that? To demonstrate with an example, we will use one of the sample OData services exposing the Northwind database. We will bind to the following hierarchical data:

Categories (data set)
	Products (data subset)
		Supplier (related sub-entity)

Each Category entity has a subset of related Product entities. When a JSON request is made to the OData service, the related Product entities are referenced by the Products field in each Category entity. I say referenced, because they are not directly available in the response containing the Categories. In an OData request, related entities and entity sets are deferred by default. We'll see what this means in the upcoming section The Internals.

Similarly, the supplier that is related to each Product is referenced by the Supplier field and is also deferred. Note that in the Category - Products relation we have a related entity set, while in the relation Product - Supplier only a single Supplier entity is related. Our databinding approach needs to be general enough to accommodate both a response containing a related entity set, as well as a single returned entity.

The Markup

Let's start with the markup. We have a RadGrid with enabled paging, sorting and filtering. It has a hierarchy of 3 detail tables - the master table to show Categories, a second level detail table to show Products and a third level detail table to show the Supplier associated with each product. Here is the complete markup:

Markup of RadGrid bound to hierarchical data from an OData service

Nothing really fancy here, this is all required for setting up detail tables with server-side databinding too. Highlights here are:

  1. There are no server-side databinding event handlers whatsoever. No DetailTableDataBind, no ParentTableRelations. This is all client-side databinding.
  2. HierarchyLoadMode for both the master and first child table are set to Client. All detail tables will be loaded and available on the client.
  3. Filtering, paging and sorting is disabled for the innermost detail table, as it will be bound to a single related entity only
  4. RadGrid.ClientSettings.DataBinding settings specify OData binding to the Categories table. This is the data source for the MasterTable only! We will be binding detail tables additionally.
  5. RadGrid.ClientSettings.ClientEvents specify 2 event handlers - this is the meat of our detail table databinding logic.

The Javascript

With the markup all set, let us dig into the javascript required to make the detail tables bind. We use 2 javascript event handlers - one for the OnHierarchyExpanding event and one for the OnCommand event. Here is what each event handlers does, and how it does it:

gridHierarchyExpanding - find the detail table when expanding a parent item and bind it

  1. Find the GridDataItem component that is associated with the expanding item.
  2. Find the detail table of the item.
  3. Bind the detail table if it is not already databound.

gridCommand - intercept commands and rebind detail tables

  1. Check if the command originates from a detail table
  2. Bind the detail table
  3. Collapse any expanded items and clear data from detail tables

The result is a 3-level hierarchical client-bound RadGrid:

RadGrid bound to hierarchical data from an OData service rendered in the browser 

A test page with the above RadGrid and all the required javascript is attached to this post. The javascript comments will help you understand what's going on. Detail tables are bound when parent items are expanded and when a page, sort or filter command is fired.

The Internals

Probably the most interesting part of the code is how the detail data is retrieved. When an OData service call returns a result set in JSON format, any related entities or entity sets are excluded. Instead, they are identified by a field named __deferred in the related entity object. Here is an example Category entity returned from the OData service we use:

A Category entity object from a OData JSON response 

As shown, the Products.__deferred.uri field tells us the URI from which we can retrieve the Products subset for this Category entity. This is exactly the data we need for the Products detail table. Each parent grid item provides us with the URI from where the data for its detail table can be retrieved.

Once we have the URI of the detail table, we can build an OData request URL that will fetch the Products for every Category item that is expanded. This is done in the bindDetailTable function. It accepts the detail table that is to be bound and the URI of the data for the table. Inside, we use a private grid method:

RadGrid._getDataServiceData(onSuccess, onFail, uri)

We usually do not recommend using private methods and fields in client components. But we also usually do not recommend going with an unsupported scenario. And this is exactly what we are doing in this blog post, so go ahead and use that method. It simply wraps a jQuery.ajax() call for you. You do not worry about the internals, you just have to pass a success and a failure callback, as well as the URI of the request.

The URL of the OData request is formed of 2 parts - the URI of the data set and a collection of URI parameters specifying data operations and a return format. For data sets that need to support paging, sorting and filtering, the GridTableView.getDataServiceQuery() method we use conveniently returns a query string with the paging, sorting and filtering state for the current detail table. You can read about supported URI parameters in the Odata URI Conventions topic. Here is a list of URI parameters RadGrid uses:

  • $format=json - used to specify the result of the query should be in JSON format
  • $skip - used to specify how many records to skip when paging (equals [page size] * [current page index])
  • $take - used to specify how many records to take when paging (equals page size)
  • $inlinecount=allpages - used to request the total row count in the database when paging
  • $order - used to specify a sort expression when sorting
  • $filter - used to specify a filter expression when filtering
  • $callback - used by jQuery for a JSONP request

When we request a single entity and no paging, sorting or filtering is supported, the need to specify $format=json as a minimum. The $callback parameter is automatically appended by jQuery.

Once we have our query string built, we concatenate it with the data URI and thus form a URL string to pass to RadGrid._getDataService(). If the request is successful, the result is then passed to the detail table and the latter is databound.

The Conclusion

Using this approach, you get an all singing, all dancing client-bound RadGrid with 3 levels of hierarchical tables. Paging, sorting and filtering is supported accross all hierarchical tables. The approach is general enough to be used with as many detail tables as you need. We use 2 client events for that. OnHierarchyExpanding is used to bind a detail table by the time its parent item is expanding. OnCommand is used to intercept any grid commands originating from a detail table and bind the detail table. Again, this is not officially supported, but it works. A test page is attached if you want to try it on your machine. Any feedback is welcome.

Download the test page 

Note: If you still haven’t tried Telerik’s ASP.NET AJAX Grid control (as well as the others in the stack), there’s no better time to do it. Take a look at all the features it supports out of the box and download a free 60-day trial with dedicated support here.

About the Author

Veli Pehlivanov

is a Technical Lead at one of Telerik’s ASP.NET AJAX teams, where he primarily works on RadGrid and a few other data- and input-centric controls, including RadListView, RadCalendar and RadInput. Veli's interests lie in the area of web development, C#, .NET, JavaScript and agile project management. He likes being on the cutting edge of technology and is keen on delivering efficient software and a greater value for the user.

Related Posts


Comments are disabled in preview mode.