9.1. Using a ListView ControlThe best way to display data in Atlas is using the ListView control (in xml-script, the <listView> element). This control can iterate through a list so that the user can view the resultthat's where the name of the control comes from. Within a <listView> xml-script element, you can define two display templates:
In addition, you set a number of attributes (which will be detailed in the following section), and can bind the data to the elements. As a target element, you can choose from any suitable HTML element. Static lists (numbered or bulleted), selection lists (<select> element), and tables are the elements most commonly used, because HTML provides these elements precisely to display a lot of data. 9.1.1. Binding Data to a ListView ControlAn obvious choice for displaying data from a server data source is an un-ordered list. The following example will query data from a server database and display it as an HTML bulleted list. Before we dig deep into xml-script, let's add the HTML markup used to display the data from the data source. First of all, you'll need a container to hold the data-display list. Here's the markup: <div > vendor list goes here</div> Next, you need to put the templates (layout and item) in a container. The style of this container will be set to invisible (display: none). Note that the data will be displayed in a different HTML element, the container that was just mentioned for holding the data, which initially functions only as a placeholder. In the layout container, we need a couple of elements (and associated IDs):
The following snippet presents an example that can be used for an un-ordered list (a <ul> element). As an outer container, a <div> element is used. The individual data item is displayed using a <li> element. As its parent element, the <ul> element can be used. This leads to the following markup serving as the placeholder: <div style="display: none;"> <!-- hide the placeholders --> <div > <!-- layout template container --> <ul > <!-- item template container --> <li ><span >vendor name goes here </span> </li> </ul> </div> </div>
Before we continue creating the page to display the data, we need to create the data to work with, which we will do by creating a web service. You need something that exposes the data you want as properties of the object returned by the web service The Atlas data binding mechanism for the listView element does not accept ADO.NET datasets directly. The two most used options are:
The custom class gives you more flexibility, but probably also means more code. Using a DataTable object, on the other hand, is rather easy: create a DataSet object and then access its Table[0] property to return the desired data table with all data in it. As noted in Chapter 1, we will use the AdventureWorks database for sample data. In this case, the fields AccountNumber and Name are from the Vendor table are queried. The code shown in Example 9-1 shows the web service that returns the AdventureWorks data as a DataTable object. Example 9-1. A web service that returns a DataTable object
Alternatively, the web service can be written to return an array of a custom type based on the data, instead of returning a DataTable object directly. Since the example requires the AccountNumber and Name fields of the AdventureWorks database, a class with two string properties must be used. The following code snippet shows how you might implement the custom type: public class Vendor { string _AccountNumber; string _Name; public string AccountNumber { get { return _AccountNumber; } set { _AccountNumber = value; } } public string Name { get { return _Name; } set { _Name = value; } } public Vendor(string AccountNumber, string Name) { this._AccountNumber = AccountNumber; this._Name = Name; } public Vendor() { } }
The web service that uses the custom type queries the Purchasing.Vendors table in AdventureWorks and selects the first 10 entries, like the first web service example does: [WebMethod] public Vendor[] GetVendors() { SqlConnection conn = new SqlConnection( "server=(local)\\SQLEXPRESS; Integrated Security=true; Initial Catalog=AdventureWorks"); conn.Open(); SqlCommand comm = new SqlCommand( "SELECT TOP 10 AccountNumber, Name FROM Purchasing.Vendor", conn); SqlDataReader dr = comm.ExecuteReader(); Then the code iterates through the list and creates a Vendor element for each entry in the data table. This list is finally converted into an array and returned from the service: List<Vendor> v = new List<Vendor>(); while (dr.Read()) { v.Add(new Vendor( dr["AccountNumber"].ToString(), dr["Name"].ToString())); } return v.ToArray(); } This example uses a construct that's new in the .NET Framework Version 2.0: generics. To be able to use generics, you have to import the associated namespaces (System.Collections for List support, and System.Collections.Generic).Example 9-2 shows the code you get in the end for the web service. Example 9-2. This web service returns a custom type
Now back to the ASP.NET page, where the web service is called. Web services will be covered in greater detail in Chapter 10, so here is just a recap of what must be done to use them (you first saw them in Chapter 1). First, the .asmx file must be referenced in the xml-script; then, a client-side proxy is generated: a local object behaving like the remote web service. That means that the local object has the same methods the remote service has; calling the local methods in turn calls the remote methods. This call is done asynchronously (just like the XMLHttpRequest calls were done in Chapter 3); once again, a callback function is used once the web service returns data. First, when including the Atlas ScriptManager, be sure to reference the web service's .asmx file. Here's the markup you need: <atlas:ScriptManager runat="server"><Services> <atlas:ServiceReference Path="ListViewVendors.asmx" /></Services> </atlas:ScriptManager> When the page has been loaded, you have to call the web service. However, the term "when the page has been loaded" is a bit misleading. The following code, for instance, would not work: <script language="JavaScript" type="text/javascript"> window.onload = function() { Vendors.GetVendors(callComplete); } </script> The load event of an HTML page occurs when the HTML of the page has been fully loaded. However at this point, it is possible that the Atlas library and the web service proxy have not been fully loaded yet. Therefore, this code could fail with a JavaScript error message such as "Vendors is not defined." Therefore it is better to add a delay. You could use JavaScript's window.setTimeout() method, or you wait and have the user click a button to get the data, using syntax like the following (the function loadVendors() will be implemented in the next step): <input type="button" value="Load Vendors" onclick="loadVendors();" /> The best way is to use the special pageLoad() method that Atlas provides: <script language="JavaScript" type="text/javascript"> function pageLoad() { Vendors.GetVendors(callComplete); } </script> Then, you can call the web service: <script language="JavaScript" type="text/javascript"> function loadVendors() { Vendors.GetVendors(callComplete); } and receive the results in the callback function. In the callback function, you have to do the following:
This leads to the following code: function callComplete(result) { document.getElementById("output").control.set_data(result); } </script> There is only one thing left to do, which is a little tricky: create the xml-script markup. Starting off is easy: create a <script> element, nest a <page> element, and then nest a <components> element: <script type="text/xml-script"> <page xmlns="http://schemas.microsoft.com/xml-script/2005"> <components> ... </components> </page> </script> Now within <components>, you can place the <listView> element. This tag requires several attributes:
The following markup is the result for the unordered list example: <listView itemTemplateParentElement > ... </listView> Within <listView>, the layout template and the item template must be defined. The former is easyyou just have to reference the outer <div>: <listView itemTemplateParentElement > <layoutTemplate> <template layoutElement="vendorsLayout" /></layoutTemplate> ... </listView> The <itemTemplate> is a bit trickier. This time, you have to reference the individual item; in the example, that's the <li> element. <listView itemTemplateParentElement > <layoutTemplate> <template layoutElement="vendorsLayout" /> </layoutTemplate><itemTemplate> <template layoutElement="vendorsItem"> ... </template></itemTemplate> </listView> Within the <template> element, you have to define the bindings for each item. Since you want to output text, you can use the <label> element, which provides a representation of the Atlas Label web control. In the markup code, the following two properties are required:
This leads to the following markup: <listView itemTemplateParentElement > <layoutTemplate> <template layoutElement="vendorsLayout" /> </layoutTemplate> <itemTemplate> <template layoutElement="vendorsItem"> <label > <bindings> <binding dataPath="Name" property="text" /> </bindings> </label> </template> </itemTemplate> </listView> A lot of work, unfortunately without any IntelliSense support. But nevertheless, the result is rewarding. Example 9-3 shows the complete markup and script for the page. Example 9-3. Binding data to an HTML list
Figure 9-1 displays the results of loading the page and clicking on the Load Vendors button. Figure 9-1. Upon clicking the button, the list is populatedWhat happens now is the following:
9.1.2. Binding Data to an HTML TableInstead of a list, you could use an HTML table to display dataan Atlas version of an ASP.NET GridView data control, so to speak. To do so, you have to change the HTML markup a bit. Instead of the <ul> and <li> elements, you need <table> and <tr> elements. Also, since a table is used now, all the data from the web service can be used, including both the Name and AccountNumber fields. For every data item, you create a table row (<tr>). Within this row, create two cells (<td>), one for each database column returned from the web service.
Here is the (hidden) placeholder to which Atlas binds server-side data: <div style="display: none;"> <div > <table > <tr><th>Account Number</th><th>Name</th></tr> <tr > <td><span >vendor account number goes here</span> </td> <td><span >vendor name goes here</span></td> </tr> </table> </div> </div> However, this will not work. Mozilla browsers do show the table, but in Internet Explorer, the browser remains blank. Internet Explorer is very particular about the structure of the dynamically generated HTML tablean interesting fact, since Internet Explorer has a history of being very gentle to incorrect HTML markup. So to make the data-bound HTML table work, you have to create the table with a <thead> and a <tbody> section. The <tbody> section is the parent element of each data item, as rendered using a <tr> element. You could also add a <tfoot> element, but this must occur before the <tbody> element. <table> <thead> <tr><th>Account Number</th><th>Name</th></tr> </thead> <tbody > <tr > <td >vendor account number goes here</td> <td >vendor name goes here</td> </tr> </tbody> </table> In xml-script, you have to add the additional binding for the new placeholder element. Then, the example works as before: when you click the HTML button, the web service is called, its result is parsed into the vendorsLayout element, and the result is copied into the ouput element. Example 9-4shows the complete code, with changes highlighted in bold. Example 9-4. Binding data to an HTML table
Figure 9-2 shows the results of displaying the page. Figure 9-2. Clicking the button generates and fills the table |