Building an Ajax Master/Detail Page with the WebDataGrid

Craig Shoemaker / Thursday, November 6, 2008

NetAdvantage 2008 Volume 3 ushered in the release of the new WebDataGrid. In order to help you get familiar with this all-new control build on the Infragistics Akido framework, I will take you step-by-step through building an AJAX master/detail page.

Activation Behavior

At the root of enabling the interaction found in this approach is the Activation behavior. When enabled activation will fire client-side and server-side events giving you hooks into controlling what happens before and after active cells are changed on the grid.

Data Source

This sample uses the BookRepository as a data source. BookRepository is a light-weight and portable way for creating data for demonstrations. Read more about the BookRepository here.

Setup the Grid

To begin you will start by setting up the WebDataGrid and adding the necessary support controls to enable interaction with the data.

  1. Add a ScriptManager to the page
  2. Next, drag a WebDataGrid from the toolbox to the page
  3. Change the ID to "dg"
  4. Set the AutoGenerateColumns to false

Next you will explicitly define the markup for the Title column. Switch to the Source view and enter the following markup inside the WebDataGrid tag:

   1:  <Columns>
   2:      <ig:TemplateDataField Key="Title">
   3:          <ItemTemplate>
   4:              <asp:Placeholder ID="Placeholder1" runat="server">
   5:                  <%# DataBinder.Eval(((Infragistics.Web.UI.TemplateContainer)Container).DataItem, "Title") %>
   6:                  <span id="b<%# this.index++.ToString() %>" class="none"><%# DataBinder.Eval(((Infragistics.Web.UI.TemplateContainer)Container).DataItem, "ID")%></span>
   7:              </asp:Placeholder>
   8:          </ItemTemplate>
   9:          <Header Text="Name" />
  10:      </ig:TemplateDataField>
  11:  </Columns>
Listing 1

This block of code creates a template column with some special elements to help power the Ajax requests.

A placeholder control is declared as a databound container so the DataBinder.Eval expressions will execute. Inside the placeholder the Title of the book is rendered to the row. The title is what is shown to the user.

Next to the Title, a span is populated with the book ID – which is hidden from the user. The purpose of the span is to contain the primary key of the record rendered to the grid. The span’s contents will be hidden from the user using a style sheet rule and will use JavaScript to read the span’s contents to get the ID value.

Getting to the container span is possible using the WebDataGrid’s client API. The current index is exposed as a method from the active cell. Therefore when a cell is activated, you can interrogate the activation data to find the current index to locate the selected item’s primary key value. You will see how this is done explicitly during the Ajax implementation below.

On line 6 of Listing 1 you will notice the following code: id="b<%# this.index++.ToString() %>". What is happening here is that the ID for each span is being assigned a unique value. The index variable is assigned in the code behind as a protected field initialized with a 0 value. This approach is a quick way to generate index values while letting the WebDataGrid’s databinding to provide the looping behavior. See Listing 2 to see this variable declared in context.

Note: Developers often like to use hidden columns to maintain state of data needed for client-site operations. Attempting to use a hidden column to hold the ID value will not work in this instance as you are using the Activation Behavior of the WebDataGrid. When Activation is enabled the user may use the tab key to advance from one active cell to the next. When a cell is active the JavaScript focus function is called in order to bring focus to the active cell. If you try to bring focus to a cell that is hidden with a stylesheet a JavaScript error is encountered as you cannot bring focus to hidden elements.

In order to hide the primary key value from the user, you must add the following style sheet entry to the header of your page:

   1:  <style type="text/css">
   2:  .none {display:none;}
   3:  </style>
Listing 2

Finally update the code behind to declare the index field and provide the grid with data:

   1:  public partial class AJAXMasterDetail : System.Web.UI.Page
   2:  {
   3:      protected int index = 0;
   4:   
   5:      protected void Page_Load(object sender, EventArgs e)
   6:      {
   7:          if (!this.Page.IsPostBack)
   8:          {
   9:              this.dg.DataSource = BookRepository.Instance.GetBooks(5);
  10:              this.dg.DataBind();
  11:          }
  12:      }
  13:  }
Listing 3

When you run the page and you should have a page that looks like this:

Basic List
Figure 1

Enable Activation

The next step is to enable the grid’s activation behavior. Using activation will give you client-side access to events that fire when a cell or row is activated. To enable activation, open the smart tag on the grid and select Edit Behaviors.

Basic List
Figure 2

When the behaviors window opens, check the box next to Activation and enter cellChanged as the function name for ActiveCellChanged under ActivationClientEvents. The cellChanged function will run when a new cell is made active.

Add Ajax Interaction

The next step is to add the client template and the JavaScript that will handle the Ajax interaction. While looking at the selected cell the routine will locate the primary key value then send request the detailed information on the selected item.

Client Template

When data is received from the Ajax call you will use a client template to display the data to the user.

Switch to the Source view and enter the following markup directly after the close tag for the WebDataGrid:

   1:  <div id="details" class="none">
   2:      <h2><a id="ttl"></a></h2>
   3:      <table cellpadding="3" cellspacing="3" 
   4:          border="1" style="border-collapse:collapse;">
   5:          <tr>
   6:              <td>Author</td>
   7:              <td id="author"></td>
   8:          </tr>
   9:          <tr>
  10:              <td>Publish Date</td>
  11:              <td id="pubDate"></td>
  12:          </tr>
  13:          <tr>
  14:              <td>Price</td>
  15:              <td id="price"></td>
  16:          </tr>
  17:      </table>
  18:  </div>
Listing 4

With the application of the class of "none" the template is initially hidden from the user. When the Ajax callback is successful the template is shown to the user filled with the appropriate data in JavaScript.

JavaScript

The next step is to implement the JavaScript required to make the asynchronous calls to the server.

Return to the Source view of your ASPX page and enter the following code in the header:

   1:  <script type="text/javascript">
   2:  var lastRow = null;
   3:   
   4:  function cellChanged() {
   5:      var grid = $find("dg");
   6:      var behav = grid.get_behaviors();
   7:      var activation = behav.get_activation();
   8:      var activeCell = activation.get_activeCell();
   9:      var row = activeCell.get_row();
  10:   
  11:      if (row != lastRow) {
  12:          var id = $get("b" + row.get_index()).innerHTML;
  13:          lastRow = row;
  14:   
  15:          PageMethods.GetDetails(id, onSuccess, onFail);
  16:      }
  17:  }
  18:   
  19:  function onSuccess(response) {
  20:      $get("details").style.display = "block";
  21:      var ttl = $get("ttl");
  22:      ttl.innerHTML = response.Title;
  23:      ttl.href = response.Url;
  24:      $get("author").innerHTML = response.Author;
  25:      $get("pubDate").innerHTML = response.PublishDateShort;
  26:      $get("price").innerHTML = response.PriceFormatted;
  27:  }
  28:   
  29:  function onFail() {
  30:      alert("Many bad things happened here.");
  31:  }
  32:  </script>
Listing 5

Starting at line 4 in Listing 5 notice the declaration of the cellChanged function. This is the function that is associated with the ActiveCellChanged event in the WebDataGrid Activation behavior.

When a cell is activated, the script traverses the grid’s object model by first finding the grid and then gaining access to the grid’s behavior. Once an instance of the behavior object is found then the script sets a reference to the activation behavior. The activation behavior exposes the active cell, which in turn exposes the row for the active cell.

Basic List
Figure 3

Note that this relationship equates to the way you add behaviors to the grid in the markup.

Once the row of the active cell is located, the function continues in an attempt to find the selected item’s primary key value found in the hidden span tag. Line 11 checks to see if the current row is not equal to the lastRow variable. This check is necessary so unneeded Ajax calls are avoided if the user has not changed rows since the last time this function ran.

Line 12 locates the primary key value in the hidden span and sets the value aside in the id variable. In preparation for the Ajax call, the current row is set aside in the lastRow variable for later evaluation.

Finally the function calls the GetDetails function which is exposed via the ASP.NET AJAX PageMethods. The arguments to the GetDetails function include the ID as well as the names of function to run if the request is successful or if it fails.

When Things Go Right

When all goes as expected, the data is returned back to the page in the form of a serialized JSON object. With this object now in scope the function will display the client template to the user and then fill the HTML elements with the appropriate data. The following is an excerpt from Listing 5:

   1:  function onSuccess(response) {
   2:      $get("details").style.display = "block";
   3:      var ttl = $get("ttl");
   4:      ttl.innerHTML = response.Title;
   5:      ttl.href = response.Url;
   6:      $get("author").innerHTML = response.Author;
   7:      $get("pubDate").innerHTML = response.PublishDateShort;
   8:      $get("price").innerHTML = response.PriceFormatted;
   9:  }
Listing 6

When Things Go Wrong

If an error is encountered during the trip back to the server, the onFail function will run. Below is the code used in this demo:

   1:  function onFail() {
   2:      alert("Many bad things happened here.");
   3:  }
Listing 7

Under normal, production grade circumstances you would want to provide a graceful way to notify the user of something going wrong. Perhaps in case of a failure in this case you may want to suggest they try the request again. For now the function simply alerts the user to the fact that something went terribly wrong in the universe.

In order for the JavaScript you’ve implemented to work properly you must also enable page methods on the ScriptManager.

Update the ScriptManager

Update the ScriptManager markup to match the following markup:

   1:  <asp:ScriptManager EnablePageMethods="true" ID="sm" runat="server" />
Listing 8

WebMethod

ASP.NET page methods make it easy for you to add Ajax behavior to your page. To support the function that the JavaScript is calling when the active cell is clicked, you must add a method to the page named GetDetails that takes a book ID as an argument.

Add the following code to your codebehind:

   1:  using System.Web.Services;
   2:   
   3:  ...
   4:   
   5:  [WebMethod]
   6:  public static Book GetDetails(int id)
   7:  {
   8:      return BookRepository.Instance.GetByID(id);
   9:  }
Listing 9

Be sure to add the using statement found in line 1 to the top of your page and then add the method inside the class definition of the page.

This method simply makes a call to our test data repository and returns an instance of a book object.

Give it a Try

Run the page and click on one of the items. You should see the grid and an item’s details presented to you:

Basic List
Figure 4

Conclusion

The WebDataGrid has a host of behaviors that are easy to enable and gain access programmatically. In this tutorial you’ve learned to add the Activation behavior to a WebDataGrid. You used the client-side events to find resources within the grid to make an Ajax call return a JSON instance of item details. Finally, you learned to use a client template for a maintainable way of presenting asynchronous data to users.

Full Code Listing

Below are links to full code listings of this demo:

Rather Watch?

Watch the following videos to see this solution being built step-by-step.

Part 1

Part 2