Platinum Partner
dotnet,asp.net,.net & windows,server-side,client-side,ajax & scripting

ASP.NET Ajax 4.0 , Master-Details View With The DataView

By now , you must have heard about the new JavaScript templating engine with the release of ASP.NET AJAX 4.0 CodePlex Preview 1 This post will show you how to use the built-in control DataView to build a master details view. As an addition , I will use an astoria service to serve the data and the Ajax Library to retrieve the data.

Consider the following model...

Parent Entity
Child Entity
 List ListElement
 Properties Properties
 ListID ListElementText
 ListTitle ListElementID
 Associations Associations
 ListElements List

The properties of the entity you are binding are represented in a syntax similar to this ... {{ PropertyName }}

To bind all lists , the template would look like this ...

<div id="listTemplate" class="sys-template">
<ul>
<li>
{{ ListTitle }}
</li>
</ul>
</div>

Retrieve data using Astoria Ajax Client Library

function loadData() {
//Create a Data Service Proxy
_dataServiceProxy = new Sys.Data.DataService("ListService.svc");
//The URI of the resources to download
var listURI = "/Lists?$expand=ListElements";
//Query the Data Service with the URI
_dataServiceProxy.query(
listURI , /*Resource URI*/
dataLoaded, /*Success callback*/
null, /*Failure callback */
null, /*user context*/
null); /*Web Request*/
}

Bind the data to the template

function dataLoaded(result,context) {
//Get the Template for the List Results
var dv = new Sys.Preview.UI.DataView($get("listTemplate"));
//Pass the data regarding the Lists to be bound
dv.set_data(result);
//Render the template
dv.render();
}

This is cool , but what about ListElements? Why not bind the ListElements in the same template?
In short , why not do this?

<div id="listTemplate" class="sys-template">
<ul>
<li>
{{ ListTitle }}
<ul>
<li>{{ ListElementText }} </li>
</ul>
</li>
</ul>
</div>

Well, it doesn’t work .
From what I can surmise ( from looking at the script code and the readme that came along with it ) , the control doesn’t allow you to bind collection properties which are complex types. In English , this means that for the type Lists, you can’t just write a template that binds all Lists, the ListElements for each List and bind Lists and ListElements at one shot.

How about we have a separate template for the ListElements? Something that looks like this...

<div id="listTemplate" class="sys-template">
<ul>
<li>
<h3>{{ ListTitle }}</h3>
<ul id="'listElementsTemplate" class="sys-template">
<li>{{ ListElementText }}</li>
</ul>
</li>
</ul>
</div>

When will I bind the internal template?

Well, it turns out that there is an event called “ItemCreated” fired by the DataView when the item is rendered.
you can add a handler to be notified of this event by calling add_itemCreated with the handler function
as a parameter. The event handler is called with the usual parameters as your .net Event Handlers.

function onItemCreated(sender,eventArgs) {
//Function body
}

Sender is the DataView which is being bound ,eventArgs is of type : Sys.Preview.UI.DataViewItemEventArgs.
call eventArgs.get_dataItem() to get the data being bound to the DataView.
call eventArgs.get_templateResult() to get the template result , i.e the template with the data filled in for the bindings.

So,  the dataLoaded function would change to be:

function dataLoaded(result,context) {
//Get the Template for the List Results
var dv = new Sys.Preview.UI.DataView($get("listTemplate"));
//Pass the data regarding the Lists to be bound
dv.set_data(result);
//add a handler to listen to the ItemCreated Event
dv.add_itemCreated(onItemCreated);
//Render the template
dv.render();
}

function onItemCreated(sender,eventArgs) {
var currentList = eventArgs.get_dataItem();
//Get the Child Template for the parent List Template
var dv = new Sys.Preview.UI.DataView($get("listElementsTemplate"));
//Bind the Template to the ListElements for the current List
dv.set_data(currentList.ListElements);
//Render the template
dv.render();
}

Well, this still doesn’t work !There is just one copy of the internal listElementsTemplate for multiple copies of the
Parent template for Lists being bound .The first one works , the second one shows us this...

[img_assist|nid=4316|title=|desc=|link=none|align=none|width=481|height=232]

"Sys.InvalidOperationException: A control is already associated with the element".

Which means that we need one copy of the internal listElementsTemplate for each copy of the ListTemplate.
So, we change the template to be:

<div id="listTemplate" class="sys-template">
<ul>
<li>
<h3>{{ ListTitle }}</h3>
<ul id="{{ 'listElementsTemplate' +ListID}}" class="sys-template">
<li>{{ ListElementText }}</li>
</ul>
</li>
</ul>
</div>

And the function to bind the listElements changes to be:

function onItemCreated(sender,eventArgs) {
var currentList = eventArgs.get_dataItem();
//Get the Child Template for the parent List Template
var dv = new Sys.Preview.UI.DataView($get("listElementsTemplate"+currentList.ListID ));
//Bind the Template to the ListElements for the current List
dv.set_data(currentList.ListElements);
//Render the template
dv.render();
}

And then , you are done !!!

Tip to debug Template Generation with IE 7

Install IE 7 Pro and you get this neat context-menu , "View Generated Source". Which lets you see the html that is generated after binding the data.

[img_assist|nid=4317|title=|desc=|link=none|align=none|width=189|height=240]

Complete sample Code is below , I swapped out the DataService code for local data. You will need the following script files.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Master Details with ASP.NET AJAX 4.0</title>
<link href="../css/SoberTable.css" rel="stylesheet" type="text/css" />
<style>
.sys-template
{
}
</style>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager runat="server" ID="scrpManager">
<Scripts>
<asp:ScriptReference Path="~/AjaxTemplate/Scripts/MicrosoftAjaxTemplates.debug.js" />
<asp:ScriptReference Path="~/AjaxTemplate/Scripts/DataService.debug.js" />
</Scripts>
</asp:ScriptManager>
<div id="listTemplate" class="sys-template">
<ul>
<li>
<h3>
{{ ListTitle }}
</h3>
<ul id="{{ 'listElementsTemplate' +ListID}}" class="sys-template">
<li>{{ ListElementText }} </li>
</ul>
</li>
</ul>
</div>

<script language="javascript" type="text/javascript">

var _dataServiceProxy = null;

function onItemCreated(sender,eventArgs) {
var currentList = eventArgs.get_dataItem();
//Get the Child Template for the parent List Template
var dv = new Sys.Preview.UI.DataView($get("listElementsTemplate"+currentList.ListID ));
//Bind the Template to the ListElements for the current List
dv.set_data(currentList.ListElements);
//Render the template
dv.render();
}

function dataLoaded(result) {
//Get the Template for the List Results
var dv = new Sys.Preview.UI.DataView($get("listTemplate"));
//Pass the data regarding the Lists to be bound
dv.set_data(result);
//add a handler to listen to the ItemCreated Event
dv.add_itemCreated(onItemCreated);
//Render the template
dv.render();
}

function loadData() {
//Create a Data Service Proxy
_dataServiceProxy = new Sys.Data.DataService("ListService.svc");
//The URI of the resources to download
var listURI = "/Lists?$expand=ListElements";
//Query the Data Service with the URI
_dataServiceProxy.query(
listURI , /*Resource URI*/
dataLoaded, /*Success callback*/
null, /*Failure callback */
null, /*user context*/
null); /*Web Request*/
}

function loadLocalData() {
var localData ={ 'd' : [
{'ListID': 1, 'ListTitle': 'Future Blog Posts to write', 'ListElements': [
{ 'ListElementID': 2, 'ListElementText': 'Working with 1..N associations'},
{ 'ListElementID': 3, 'ListElementText': 'Working with ServiceOps and the client'},
{ 'ListElementID': 4, 'ListElementText': 'Data Literal Table'},
{ 'ListElementID': 5, 'ListElementText': 'Calling Stored Procedures from ServiceOps'} ]
},
{'ListID': 2, 'ListTitle': 'My Life List',
'ListElements': [{'ListElementID': 64, 'ListElementText': 'Learn Spanish'}] }
] };

dataLoaded(localData.d);
}

function pageLoad() {
loadLocalData ();
}
</script>

</form>
</body>
</html>

Original Author

Original Article Written By Phani Raj

Published at DZone with permission of {{ articles[0].authors[0].realName }}, DZone MVB. (source)

Opinions expressed by DZone contributors are their own.

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}