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..
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>
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 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>
function onItemCreated(sender,eventArgs) { //Function body }
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 .. “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.
<!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>
This is great, but I have a couple of comments. First, you can use the $id function to generate the ids, and second you shouldn't call render on the data lists but rather initialize.
This is great, but I have a couple of comments. First, you can use the $id function to generate the ids, and second you shouldn't call render on the data lists but rather initialize. Also, it would be simpler if you set localData to a literal directly intead of creating a JSON string and deserializing it.
If you are just tuning in , you can read Part 1 here Parent Entity Child Entity List Properties       
1. 44个令人惊奇的Silverlight视频教程。 2. Expression Encoder 2 VB.NET update 3. HD View for Internet Explorer HD
This week I am coming to you from the Microsoft Campus. So as you would expect I have a lot of energy