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):

image 

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:

image

Download the sample code here.

Also, be sure and check out these other awesome posts about Microsoft Ajax 4.

These are based on previous preview releases.

16 Comments

  • very nice article. but the attachment is empty, only web.config inside. could you update it?

  • Ricky -- oops! Sample code fail. It's updated now. Thanks for noticing..

  • thanks.
    it sounds great!

  • This is great! Add more exciting applications.

  • Combined with live bindings this is just pure WIN.

    Looking forward to using this stuff, I'm hoping it can be used to create forms on the fly from data including info about the validation of fields.

  • Can I exec logic within dataview template when binded collection is empty? For example.


    message goes here

    0 }}">
    cart items go here



    As I understand when collection is empty dataview items will not be rendered at all.

    cartObject.get_items() returns observable collection.

  • Sergei --

    First of all, FYI, the collection doesn't need to be observable, thats a purely optional step you would do if you just prefer that method over using the equivilent static Sys.Observer methods.

    DataView currently has no 'no data' template logic built into it. We're looking at improving that, so no worries. The way you can do it now though is to hookup a separate UI area that is only visible when the length is not 0, using a binding. Assuming the array returned by get_items isn't being thrown away and rebuilt, you could do something like this:


    (style)
    .hidden { display: none }

    only visible when no items

    Binding to 'class-foo' works by applying the class if the given statement is true, removing it if it is false. In this case, it is a binding, so it updates on the fly whenever that items collection is added to / removed from. Should work wonderfuly, let me know if you have any problems.

  • Very nice article. Can you please let us know whether Microsoft Ajax uses proxy js classes for fetching the data from server ?

    thanks,
    Thani

  • That's just the thing for me.

    Many thanks!

  • Hi, I have a quick (sort of related) question. Using Firebug with a page loaded with the Microsoft Ajax framework, how can I quickly see the version of the framework loaded? In jQuery I would do:

    $().jquery;

    To get "1.3.2"

    Thanks!

  • I think the imperative way of binding will be faster too? Since the frameworks doesn't need to parse the html and all.

  • Mike -- normally the version number is in comments at the top. That isn't the case in this preview release I'm afraid.

    Imperative faster than declarative? Yeah, for sure. However, I think you'd be pleasantly surprised at the performance of the declarative stuff. Unless you have a huge page with a ton of stuff, it's pretty much a non issue.

  • Very nice post describing the DataView control. I feel the resources mentioned in this article are worth visiting as well. So three cheers mate. Congrats!

  • This is cool. How difficult would it be to implement paging?

  • Nice article Dave, its really interesting. I am going to try this out right away .....cheers man

  • Thanx for the information. I did not know that AJAX has its own scripts for Blogging facility. Please provide more information over it. Provide links to related topics if possible.

Comments have been disabled for this content.