This is the second in a series of blog posts on the new RadListView client-side databinding features. In the first post, we introduced you to the basics of HTML templates, binding expressions and the databinding model in RadListView. In this post, we will see how RadListView supports automatic (read codeless) databinding to web services and JSON feeds.
For a quick reference, here is a brief list of the sections in this blog post:
Now, this is not the first time we write client-side support for automatic web service binding. RadGrid, RadScheduler, RadComboBox, RadMenu, RadTreeView, RadNotification, RadRotator, RadTagCloud are only some of the more popular controls that can bind on the client-side if you have a web service to feed the data. With RadListView, we wanted to take the flexibility to the next level. We created a client-bound control that doesn't care what kind of web service you have at hand. ASMX, WCF, Page Methods, OData or another type - RadListView will chew the data no matter where it's coming from. The only requirement - respond in JSON or JSONP.
To tell RadListView you have a web service to serve the data, you provide the location
of the JSON service to RadListView as a value of the RadListViewClientSettings.DataBinding.DataService.Location
property. Depending on your service, you may need to configure additional settings,
but let's discuss these in a minute. When RadListView is initialized on the client,
it checks if service Location
is provided along with a client-side
ItemTemplate
. If both are present, the control initializes web service
parameters and calls your service. Client-side databinding in RadListView is heavily
jQuery-based.
jQuery.ajax() is used to make a request to the web service and parse the
result. If a JSON result is properly received back, the control takes the data from
the response and databinds automatically.
This happens on initial load without any developer intervention. When, for any reason, RadListView requires rebinding, it fires its client-side OnCommand event. The OnCommand event is an extension point for the developer, where databinding can be altered or canceled altogether. Users of RadGrid and its client-side databinding capabilities will find themselves at home, as RadListView follows the same client-side databinding life cycle for consistency.
If you need to explicitly rebind RadListView, you call RadListView.rebind()
.
This, again, causes RadListView to fire its OnCommand
client event
and make a web service call to refresh the data. Note, that RadListView.rebind()
and RadListView.dataBind()
are similar in result, but completely different
in inner working. rebind()
will cause the control to make a web service
call. If data is properly retrieved, dataBind()
is called internally
to actually perform the databinding if there is a valid data source. Additionally,
rebind()
is what you would call if you had server-side databinding
and wanted to make RadListView postback and rebind on the server. dataBind()
doesn't work with server-side databinding at all.
Any time RadListView is databinding, it fires its client-side OnDataBinding
event. This is another extensibility point for the developer. When binding to web
services, you can use this event to alter not only RadListView control settings,
but also the AJAX parameters that will be used to make a service call. The OnCommand
and OnDataBinding
events together provide a mechanism for easily changing
any detail around RadListView's databinding, making databinding a very flexible
feature.
What we discussed so far can be generalized in a neat little state chart diagram, describing the client-side databinding life cycle in RadListView:
What does it tell us? We have provided 2 extension points - the OnCommand
and DataBinding
client events. Both can be canceled to prevent databinding
or implement some custom binding programmatically. Both can be used for changing
the binding parameters, though at a different level. After the DataBinding
event, jQuery takes up and makes an AJAX call to the web service. After the AJAX
request, databinding can potentially fail if there is something wrong with the response.
If JSON data is properly received, actual databinding happends and list view items
are re/created. Databinding finishes off with the DataBound
event.
You can use this event to manipulate the DOM at the earliest possible time after
databinding.
An important note to make here: the above state chart diagram describes the databinding
life cycle in RadListView when no data caching is enabled. RadListView additionally
supports data caching when binding to web services. When data caching enters the
game, RadListView can skip the path from jQuery.ajax()
to JSON Response
and go straight to databinding, if there is a matching cache entry for the current
state of the list view. More on caching later.
To enable automatic web service binding, RadListView provides a set of properties
that control various aspects of the web service request. These settings are collectively
placed under RadListView.ClientSettings.DataBinding.DataService
:
These may seem like a lot of properties to consider, but don't worry, a lot of databinding
scenarios work with default values for most of these settings. Let's go over a couple
of the more important ones. Obviously, Location
is where you always
start with. You need to set it to the URL of your web service. This URL can be relative
or absolute, for local or remove web services, respectively. When working with method-based
web services like ASMX, WCF services or page methods, DataPath
specifies
the name of the method that will be called to retrieve the data. DataPath
is not required, though, you can, just the same append, the method name to the end
of the URL specified in Location
.
CountPath
is an interesting story. Some services provide one method
(or URI) for the data and another for the total row count. When binding to such,
the DataPath
property specifies the method name to call when total
row count is required. DataPath
is always appended to Location
to form the final URL, so no absolute URLs here. Total row count is required when
paging is enabled in RadListView, so depending on the web service, RadListView can
retrieve the total count either from a field in the response coming from the main
data request, or from a separate count request. If you are designing your web service
yourself, I don't see a reason for using a separate count method and excessively
require a second request. But when using a web service you cannot modify, and it
requires a separate count request, the CountPath
property enables RadListView
to look for the total item count with an additional service call.
One of the most important properties when working with web services is HttpMethod
.
RadListView can request web services using both GET
and POST
HTTP methods. Choosing the right HTTP method is very important, because it determines
every other detail around calling your web service. By default, RadListView makes
POST
requests to a service. Request parameters are sent as post data
in the form of a JSON-encoded string. If you change this property to Get
,
RadListView starts making GET
requests, where request parameters are
sent as URL-encoded query string values appended to the service URL. Equivalently
importantly, the HttpMethod
determines the default values for many
of the above listed properties. Following is a comparison table that lists the properties
affected by HttpMethod
and their default values depending on the selected
HTTP method:
Setting | Value when POST | Value when GET |
---|---|---|
DataPropertyName | "Data" | "results" |
CountPropertyName | "Count" | "__count" |
FilterParameterName | "filterExpression" | "$filter" |
FilterParameterType | List | OData |
SortParameterName | "sortExpression" | "$orderby" |
SortParameterType | List | OData |
StartRowIndexParameterName | "startRowIndex" | "$skip" |
MaximumRowsParameterName | "maximumRows" | "$top" |
As you can see, web service parameters vary drastically depending on the selected
HTTP method. Let's assume paging, sorting and filtering are all enabled in RadListView
when databinding to web services. When using POST
requests, RadListView
will send the data in the following format:
{"startRowIndex":0,
"maximumRows":8,
"sortExpression":"ProductID",
"filterExpression":"CategoryId == 4"}
For an equivalent set of paging, sorting and filtering parameters, here is what
query string value will RadGrid append to the web service URL, when making GET
requests:
$skip=0&$top=8&$orderby=ProductID&$filter=CategoryId%20eq%204
Quite bit of a difference just changing the HttpMethod
, right? People
familiar with the OData protocol
will immediately recognize the OData-specific URL parameter names. That's right,
RadListView defaults to OData support when using HttpMethod="Get"
.
That desn't, however, mean that no other GET
-accessible (or RESTful)
JSON services are supported. These are just defaults and everything is configurable.
Similar to the request parameters, RadListView expects the response in a different format, depending on the HTTP method used. With a POST request, the default expected JSON response is either:
{ Data: [], Count: 0 }
or
{ d: { Data: [], Count: 0 } }
where the Data
field is an array containing the data items and Count
is the total count. Count
field can be missing and if you provide the
DataPath
property, RadListView will make a second request for the total
row count.
When HttpMethod="Get"
, the default expected JSON response is:
{ d: { results: [], __count: 0 } }
As noted previously, this is what an OData service would return. The results
contains the data items and __count
specifies the total row count.
__count
may also be omitted, but the CountPath
property
does not work with GET requests. RadListView won't make a second request to get
the total row count when HttpMethod="Get"
.
Let's see some databinding scenarios in action. We've assembled all examples in a small test project you can download. The examples are rather trivial, but the idea is to get you going with the client-side databinding basics, not to come up with a full-fledged Twitter feed widget. We can do that later on.
Let's define a simple RadListView layout and bind it to an ASMX service:
Our service definition is basic - a couple of methods to return some data and count:
Running the page, we see a couple of boxes on the screen:
We've just created our first automatically databound RadListView. How difficult was that?
Binding RadListView to a WCF service is no more different. Let's examine a simple WCF service that returns the same data as our previous ASMX service.
This time, we return the data and count in a single request. This means RadListView
will require only the Location
and DataPath
properties
and no CountPath
:
Running the page, we get the same set of square boxes rendered on page. They look ugly, I know, but we'll have plenty of talks on how to beautify your dynamically bound HTML, I promise.
By now, you get the idea. Default HTTP method for service requests is POST in RadListView.
ASMX services, WCF services and ASP.NET Page Methods use POST requests, so no additional
configuration required. All we need is Location
, DataPath
and optionally CountPath
. In fact, we don't even use total row count
in the examples in this blog post, but I showed you how you can configure RadListView
to request it. In our next blog post in this series, we'll see how total row count
can be used for paging along with a couple of other nice features.
Let's try binding RadListView to an OData service now. There are a couple of publicly
available OData services out on the web. Let's use the Customers table from the Northwind OData service available
at odata.org. Apart from being
a GET-accessible web service, this is also a remote web service. This means
that we need to configure RadListView for JSONP binding. Let's keep the example
simple again and bind only the CustomerID
field:
Looking at the DataService
settings, they tell us that we are using
an OData service URL to access the Customers
entity set. We have also
specified that we need to make GET requests to acces the service and that we want
to use JSONP
for accessing the data. JSONP is a convenient way of accessing a JSON-formatted
data feed on a remote host. The details are explained in any JSONP-related article
out on the web.
Save, run and we get the ProductID data values rendered in our browser, coming fresh from odata.org:
Behind the scenes, RadListView uses jQuery to make a GET request to the web service, specifying it wants to use the JSONP format and passing the required callback parameter. Here is the actual URL that gets called:
http://services.odata.org/Northwind/Northwind.svc/Customers?$format=json&$callback=[...]&_=[...]
jQuery knows how to make a JSONP request, passing $callback
required
for the JSONP exchange protocol and a cache buster parameter with a single underscore
in the name. The rest is just boring databinding details RadListView hides from
you. Automatic databinding to OData is three properties away. How easier can this
become?
A couple of times already I mentioned that every detail around the client-side databinding in RadListView is configurable. Let me prove my words and bind RadListView to a very generic JSON feed. This is neither ASMX, nor WCF, nor Page Methods, nor ODAta. This can be any general data feed that can respond in JSON (or JSONP for remote access). It can vary in supported HTTP methods, parameter names, or the JSON structure in the response. Let's create a quick and dirty JSON feed by simply implementing a generic HTTP handler in our test project:
Admittedly, this may not be the best way to create a JSON data feed, but it does the job. More interestingly, my JSON service does not follow any previously discussed convention for the JSON response. If I browse to the service URL, I get the following response:
The wrapper JSON object contains a field named Result
. This object,
in turn, holds the data array in a Data_Items
field and the total row
count in a Total_Item_Count
field. To make RadListView properly bind
to this feed, we need to specify a custom DataPropertyName
and CountPropertyName
in the markup:
Note the property nesting. I instruct RadListView to search for a nested object
in the Result
field of the JSON response, then find the data array
under Data_Items
in this nested object. RadListView can parse nested
property names and search for the data and count at an arbitrary nesting depth.
This makes it very flexible for binding to any general JSON feed no matter where
in the response is the data or total count nested.
As implemented, the above HTTP handler will accept both GET and POST requests. This means I can leave the default POST HttpMethod in RadListView.
Running the page with the above defined RadListView, we get the same old numbered squares - an indicator that RadListView has successfully databound to our custom feed:
With the flexible client-side databinding capabilities of RadListView, you are not restrained to strictly using enterprise-level web service standards like WCF, OData and the like. Twitter, YouTube, Facebook and myriads of other JSON service APIs out there are at your reach in no time.
In the previous blog post in the series, I promised to show you how you can define a client-side layout and bind to web services automatically without writing any javacript. In this blog post, we created 4 sample databinding scenarios, connected to 3 different local web services and 1 remote OData service, yet wrote 0 lines of javascript. How many lines of javascript does it usually take you to create even the simplest of client-bound UI? Zero is the exact amount it took me with RadListView. Download the sample application I worked on in this project and try it yourself.
In this blog post, we introduced you to the automatic web service databinding features RadListView provides out of the box for you. For the sake of keeping the examples simple, we can't claim we've shown you anything colossally eye-pleasing. In the next blog post in this series, we'll try to fix that up. Along with some proper looking, client-bound tables and lists, we'll dive deeper into some advanced databinding features in RadListView. Paging, sorting, filtering, and item selection are some of the topics that we'll cover.
In the mean time, share your opinion, message, praise or critique in the comments section below. Your feedback on what we've been working on for some time now is important for us. Are we on the right track? Do we make working with client-side data easier for you? Would you rather have us do something else to ease your client-side coding pains?
This stuff is all at your reach now with the Q1 2012 release of RadControls for ASP.NET AJAX. Check out the demos or download a trial to experience the power, flexibility and ease of use with one of the most complete UI component suites on the market.
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.