Microsoft Ajax 4 Preview 5: The DataView Control
Preview 5 of the Microsoft Ajax 4.0 library has been released. Some quick background – this the next version of the client-side ajax framework you have probably already heard of, the one that ships along with ASP.NET 3.5 (but is also available in script form). The fact it ships with ASP.NET has sometimes led to it being called the ASP.NET AJAX Framework. Technically, that name is a superset – it encompasses “Microsoft Ajax” and it’s ASP.NET specific, server-side features, like the ScriptManager. But “Microsoft Ajax” always has and always will be an ajax framework that is not tied to any specific server-side technology. Also noteworthy is that this 4.0 library can run on top of ASP.NET 3.5, replacing the 3.5 version it normally uses.
In previous previews of Microsoft Ajax 4, we introduced the DataView control. It’s a simple but powerful control that takes advantage of the client templating engine we built for it. You point it at a template, and some data, and it instantiates the template for each item in the data. The templating engine lets you use ‘placeholders’ and create ‘live bindings’ between the rendered DOM elements and the data, as well as allow you to attach other ajax components and controls.
I want to focus on one of the new features added to the DataView in preview 5, but first, I feel obliged to introduce the DataView control from a basic level, since most of you probably don’t know about it yet.
Simple DataView Example
As a simple example – lets make up some data. Normally this would come from a web service or some other source, not hard coded, of course.
var stockData = [
{ symbol: "DJIA" , change: 79.88, value: 9627.48 },
{ symbol: "NASDAQ", change: 23.63, value: 2084.02 },
{ symbol: "AAAA" , change: -0.22, value: 27.56 },
{ symbol: "BBBB" , change: -1.46, value: 82.12 },
{ symbol: "CCCC" , change: 0.67 , value: 7.56 }
];
We want to show this stock data in a table, and probably do some formatting to show the values as currency, etc. Without a templating engine, you’d basically either have to painstakingly create DOM elements by hand, or build up a string and use innerHTML. Neither technique is easy to maintain, or are designable by designer tools. Worse, building a string and using innerHTML could net you some XSS vulnerabilities. This is where the templating engine in Microsoft Ajax 4 comes in.
<table id="stocks" class="stocks sys-template">
<tr>
<td>{{ symbol }}</td>
<td>{{ value.localeFormat("c") }}</td>
<td>{{ change.localeFormat("c") }}</td>
<td>{{ (change/value).format("p2") }}</td>
</tr>
</table>
In the simple form, {{ foo }} represents a placeholder where the value of a field named ‘foo’ from the data items will be. What language is this stuff? Just javascript. So as you can see, this take advantage of the formatting methods in Microsoft Ajax to convert the values to currency. You could write any ole javascript here, of course (unobtrusive javascript alert: yes, you can do it that way too, more on that later).
The class “sys-template” is setup to have display:none, so that this chunk of HTML doesn’t actually show up in the markup.
That’s the template – it represents what each item should look like. Now to put it to use with the DataView control:
function pageLoad() {
$create(Sys.UI.DataView, {
data: stockData
}, null, null, $get("stocks"));
}
That’s it. The DataView control is given the data, and the template. Everything is hooked up. Here is the result (css not shown):
One obvious improvement we could make to this sample is to dynamically style the rows so that stocks going up are green, down, red. You can in fact do that, but I digress. This is just the most basic use of a DataView. I haven’t even mentioned live bindings. And even in preview 4 you could reuse templates across multiple dataviews, and set the placeholder to be something other than the default. Here is an excellent article by Jim Wang which covers some of the other things you could do in Preview 4, including selecting items, and integrating with an ADO.NET DataService.
http://weblogs.asp.net/jimwang/archive/2008/11/05/working-with-ado-net-data-services-in-ajax.aspx
Preview 5 – Dynamic Templates and Dynamic Placeholders
Preview 5 introduces a new feature to the DataView control that allows you to dynamically determine the template used for each data item, as well as dynamically determine the placeholder that anchors where it will be inserted. This means each data item can potentially render completely differently, and render into completely different areas of the page, all the while being under the control of a single DataView!
What does this mean? Well, say you have a single set of data returned by a service of some kind. But you don’t want to simply list this data all in one place. Some needs to go there, and some over there, depending on the state of each item. A typical example of this might be a data set of forum posts, where some of them are marked as ‘sticky’, and so should be listed first, but the sticky ones aren’t necessarily first in the list of data. How would you normally deal with that? Separate the sticky items into their own data set, or query for them separately? If you are using stored procedures, maybe that means creating a new one or modifying an existing one to support the filtering. Why go through such hoops and database-level manipulations when this is purely a UI problem? If it can’t be solved by your UI tools, perhaps you’re blurring your separation of concerns.
So as an experiment, I imagined a list of bloggers. Each blogger may or may not be on Twitter, right? But I want the tweeting and non-tweeting bloggers to be listed separately. Furthermore, the way I display a tweeting blogger and a non-tweeting blogger may be very different. For one, I want to display some of the latest tweets from the tweeting bloggers, and display their Twitter avatar.
Mind you, it is certainly possible to use dynamically determined css classes, and to dynamically show/hide regions of content within a template, based on the data item. But that only gets you so far. Sometimes, the differences are too much to neatly define a single template that can represent either kind of data item. This particular example could have been done either way. Maybe I just wasn’t creative enough :)
First – let’s define our bloggers.
var bloggerList = [
{ name: "InfinitiesLoop", author: "Dave Reed",
uri: "http://weblogs.asp.net/infinitiesloop", twitter: "infinitiesloop" },
{ name: "Computer Zen", author: "Scott Hanselman",
uri: "http://www.hanselman.com/blog", twitter: "shanselman" },
{ name: "Random Thoughts", author: "Bob",
uri: "http://myblog.com" },
{ name: "Tales from the Evil Empire", author: "Bertrand Le Roy",
uri: "http://weblogs.asp.net/bleroy", twitter: "bleroy" },
{ name: "The Gu", author: "Scott Guthrie",
uri: "http://weblogs.asp.net/scottgu", twitter: "scottgu" },
{ name: "Some blog", author: "Mr. IDontTwitter",
uri: "http://weblogs.asp.net/donttwitter" },
{ name: "Jim Wang's Blog", author: "Jim Wang",
uri: "http://weblogs.asp.net/jimwang/", twitter: "turanuk" },
{ name: "James Senior", author: "James Senior",
uri: "http://www.jamessenior.com/", twitter: "jsenior" }
];
Each blogger has a blog name, author name, uri, and if they have one – a twitter id. Important to note – the non-twittering bloggers are not at the beginning of the array, they are mixed in with the others.
For brevity I’m going to show the declarative way of attaching a DataView control, even though we feel most developers would prefer to keep this stuff separated from their markup. Everything you see here can be done in pure code (imperatively). And we are actively working on some very interesting improvements that make the imperative ways much, much easier than they are in Preview 5, so stay tuned, you won’t want to miss it. Both declarative and imperative approaches are in the sample download at the end of this post.
Let’s first define the overall structure we want:
<div id="bloggers" class="bloggers"
sys:attach="dv" dv:data="{{ bloggerList }}"
dv:onitemrendering="{{ itemRendering }}">
<div class="notwitter">
Non-twittering Bloggers
<ul>
<li id="normalph"></li>
</ul>
</div>
<div class="hastwitter">
Twittering Bloggers
<ul>
<li id="twitterph"></li>
</ul>
</div>
</div>
Two divs, one for each kind of blogger. And placeholders for where each should be rendered. A DataView is attached declaratively with sys:attach=”dv” (the ‘dv’ comes from an xml namespace declared on the documents body tag and maps the namespace ‘dv’ to the Sys.UI.DataView class, ‘dv’ is not magical). As each blogger is processed, the ‘itemRendering’ event is fired before anything is instantiated for it. We’ve hooked up a handler for it:
function itemRendering(dv, args) {
var blogger = args.get_dataItem();
if (blogger.twitter) {
args.set_itemTemplate("twitterblogger");
args.set_itemPlaceholder("twitterph");
}
else {
args.set_itemTemplate("normalblogger");
args.set_itemPlaceholder("normalph");
}
}
If the blogger has a Twitter id, we set the template to the ‘twitterblogger’ template and tell it to render where the ‘twitterph’ element is. Otherwise, we use the ‘normalblogger’ template and render them where the ‘normalph’ element is. For a ‘normal’ blogger, we just want to render a link to their blog and their name:
<ul class="sys-template" id="normalblogger">
<li>
<a sys:href="{{ uri }}">{{ name }}</a>
<span>author: {{ author }}</span>
</li>
</ul>
Pretty simple. For a tweeting blogger, we want to do something slightly different, plus we want to show their last 5 tweets for good measure.
JSONP
Another feature in Microsoft Ajax 4, I should mention, is JSONP support. And good thing too, because it makes this demo way cooler. Twitter happens to have a JSONP service, and the DataView integrates nicely with the networking support in Microsoft Ajax, giving it JSONP support, too. So getting tweets for someone is just a matter of creating a DataView with the JSONP address as the ‘dataProvider’. So our Blogger DataView is going to actually contain a nested DataView. Here is the ‘twitterblogger’ template:
<ul class="sys-template" id="twitterblogger">
<li>
<a sys:href="{{ uri }}">{{ name }}</a>
<span>{{ author }}</span>
<ul class="tweets sys-template" sys:attach="dv" dv:autofetch="true"
dv:dataprovider="{{ getTwitterUrl(twitter) }}">
<li>
<img class="avatar" sys:src="{{ user.profile_image_url }}" alt="" src="" />
{{ text }}
</li>
</ul>
</li>
</ul>
So, our blogger entry has a nested UL, to which we attach another DataView with the JSONP Twitter url as the target provider. The data returned from Twitter also has information about the Twitter user, like their avatar, so we may as well show that, too. The ‘getTwitterUrl’ call demonstrates that you aren’t limited to just data – you can do stuff with it too. That method simply takes the blogger’s Twitter id and constructs the JSONP service url for their last 3 tweets.
That’s it! Here it is in action:
Download the sample code here.
Also, be sure and check out these other awesome posts about Microsoft Ajax 4.
- Bertrand demonstrates how to use templates recurisvely with the DataView to display hierarchical data.
http://weblogs.asp.net/bleroy/archive/2009/09/14/building-a-class-browser-with-microsoft-ajax-4-0-preview-5.aspx - Jim Wang shows how to make MS Ajax 4 compatible with ASP.NET 3.5's UpdatePanel, and some simple ado.net integration.
http://weblogs.asp.net/jimwang/archive/2009/09/11/asp-net-ajax-preview-5-and-updatepanel.aspx
These are based on previous preview releases.
- Preview of the DataView (based on Preview 4) by Encosia
A sneak peak at ASP.NET AJAX 4.0's client-side templating Encosia - Damien White has an excellent series of articles (based on preview 4)
DataView: http://blogs.visoftinc.com/archive/2009/04/28/ASP.NET-4.0-AJAX-Preview-4-Client-Templates.aspx
Sys.Observer: http://blogs.visoftinc.com/archive/2009/05/21/ASP.NET-4.0-AJAX-Preview-4-JavaScript-Observer-Pattern.aspx
Bindings: http://blogs.visoftinc.com/archive/2009/05/27/ASP.NET-4.0-AJAX-Preview-4-Data-Binding.aspx - I blogged about another feature in Microsoft Ajax 4, Sys.Observer (based on preview 3 but relevant)
http://weblogs.asp.net/infinitiesloop/archive/2008/11/09/asp-net-ajax-4-0-observing-updates-to-pojos-plain-ole-javascript-objects.aspx