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
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
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.
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:
Nothing really fancy here, this is all required for setting up detail tables with server-side databinding too. Highlights
event and one for the OnCommand event. Here is what each event handlers
does, and how it does it:
The result is a 3-level hierarchical client-bound RadGrid:
tables are bound when parent items are expanded and when a page, sort or filter
command is fired.
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:
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
Odata URI Conventions topic. Here is a list of URI parameters RadGrid uses:
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.
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
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#,
of technology and is keen on delivering efficient software and a greater value for