Click here to Skip to main content
15,878,959 members
Articles / Web Development / ASP.NET
Article

Accessing the data of ASP.NET templated controls from client-side script

Rate me:
Please Sign up or sign in to vote.
4.06/5 (9 votes)
17 Jul 2005CPOL5 min read 69.6K   51   6
The article demonstrates an easy method to access the data of templated controls (Repeater, DataList, DataGrid) from client-side scripts.

Sample Image - ControlClientScript.jpg

Introduction

This article demonstrates an easy method to access the data of templated controls (Repeater, DataList, DataGrid) from client-side scripts. This will allow us to use this data in client-side operations such as calculation and validation.

Background

Templated controls are a special type of data-bound server controls that offer great flexibility for rendering list-like data. At design-time, a templated control enables you to use a combination of HTML and server controls to design a custom layout for the fields of a single list item. And at run-time, it binds to the data source, walks through the bound items, and produces graphical elements according to the template you designed.

.NET framework 1.0/1.1 ships with three templated controls, the Repeater, the DataList, and the DataGrid. You can also build your own templated control by implementing the INamingContainer interface.

A challenge you may encounter when using templated controls is how to access the data in the generated child controls from client-side script, because the client-side does not understand templates, it only understands the names of individual controls, and it’s hard to guess what the name of each of the child controls will be at run-time.

The problem

I’ll use a simple example to explain my idea. Let’s assume we have a database table that stores employee expenses over a certain period. The table has the following schema:

IDint
ExpenseTypevarchar
ExpenseAmountdecimal

We want to allow the employee to enter the amount he spent on each expense type using a DataGrid control. As the employee updates the amount of each expense, it is required to display the total amount he entered, and to validate that this total does not exceed 1000.

These requirements are easy to achieve if we decide to make the updates row by row. This means that as the employee updates each row, the form has to make post-backs to the server to calculate the new total, and apply the validation rule.

The client-side approach

My proposed approach has several benefits, as it conserves the server and network resources, and provides a more interactive user interface.

The DataGrid used has the following structure:

ASP.NET
<asp:datagrid id="dgExpenses" runat="server" AutoGenerateColumns="False">
<Columns>
  <asp:BoundColumn DataField="ExpenseType" HeaderText="Expense Type">
  </asp:BoundColumn>
  <asp:TemplateColumn HeaderText="Expense Amount">
   <ItemTemplate>
    <asp:TextBox Text='<%# DataBinder.Eval(Container.DataItem,"ExpenseAmount")%>' 
    Runat="server" ID="txtExpAmount"></asp:TextBox>
   </ItemTemplate>
  </asp:TemplateColumn>
 </Columns>
</asp:datagrid>

The code that populates the DataGrid is straightforward:

C#
SqlConnection conn = new 
     SqlConnection("server=localhost;database=pubs;uid=sa;pwd=");
SqlCommand cmd = new SqlCommand("select * from timesheet", conn);
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
dgExpenses.DataSource = rdr;
dgExpenses.DataBind();
rdr.Close();

To expose the data to the client-side script, I use a hidden field to store the initial values of the ExpenseAmount field as a string of comma-separated values.

C#
hdnRowArr.Value = "";
string expAmount;
foreach(DataGridItem dgi in dgExpenses.Items)
{
    if(dgi.ItemType == ListItemType.Item || 
       dgi.ItemType == ListItemType.AlternatingItem)
    {
        expAmount = ((TextBox)dgi.Cells[1].Controls[1]).Text;
        hdnRowArr.Value += expAmount + ",";
    }
}
hdnRowArr.Value = hdnRowArr.Value.TrimEnd(',');

In the client-side, I split the comma-separated values into an array. Using an array makes it easier to update the values when the user makes a change. It also simplifies calculating the total. I use the following function to fill the array. Then I calculate the initial total to update the UI. This function is invoked in the Body OnLoad event.

JavaScript
function refreshArray()
{
    var commaSeparated = document.getElementById("hdnRowArr").value;
    arrRows = commaSeparated.split(",");
    sumArray();
}

Now we need a way to detect any changes made by the user, update the array, and re-calculate the total. I add the following code to the DataGrid ItemDataBound event.

C#
if(e.Item.ItemType == ListItemType.Item || 
     e.Item.ItemType == ListItemType.AlternatingItem)
{
    int rowIndex = e.Item.ItemIndex;
    TextBox txtExpAmount = (TextBox)e.Item.Cells[1].Controls[1];
    string evtHandler = "updateValue(this," + rowIndex.ToString() + ")";
    txtExpAmount.Attributes.Add("onblur", evtHandler);
}

This code will add an event handler for the onblur event of each TextBox in the ExpenseAmount template column. The event will be handled by the updateValue client-side function. I pass to the function a reference to the child control, and the index of the row that contains the control. The index is used to locate the array element that corresponds to the DataGrid row.

JavaScript
function updateValue(field, rowNum)
{
    arrRows[rowNum] = field.value;
    sumArray();
}

Calculating the total and updating the UI is done by this function:

JavaScript
function sumArray()
{
    var sum = 0;
    var expAmount;
    for(var i = 0;i < arrRows.length; i++)
    {
        expAmount = parseFloat(arrRows[i]);
        if(!isNaN(expAmount))
            sum += expAmount;
    }
    document.getElementById("txtTotalExp").value = sum;
}

Finally, we can associate any validator with the txtTotalExp TextBox. We have to set the "ReadOnly" property of the TextBox to "true" to prevent the user from updating the total manually. So, now with every change to the values of the ExpenseAmount TextBoxes, the total will be automatically re-calculated.

Using the code

In order to apply this method for any templated control, you can follow these steps for each column you want to access from the client-side:

  1. Add a hidden field, and fill it with the initial data that are retrieved from the data source. Pass the data to the field as comma-separated values.
  2. Add an Array variable, and the corresponding refreshArray, updateValue, and sumArray JavaScript functions to the form. You can replace the sumArray function with another function to perform any other operation than calculating the total.
  3. In the Body OnLoad event, add a call to the refreshArray function.
  4. Use the ItemDataBound event of the templated control to add a call to the updateValue function to handle the child control client-side event.
  5. Add a Label or a TextBox to display the result of the client-side operation. You can then add a suitable validator and associate it with the TextBox.

Points of interest

  • The client-side approach can be extended to address a wide range of templated control validation requirements. For example, if you have a DataGrid that has a template column with a CheckBox (shown in picture), and you want to put a limit on the maximum or minimum number of CheckBoxes that can be checked, it can easily be implemented by applying the same concept introduced here. This example is included in the sample code.

    Sample screenshot

  • It is important to note that the goal of having the validation or any other operation done on the client-side is not to move a part of the data processing from the server to the client, but rather to avoid the repetitive round trips to the server while the user is entering the data. So in the example I used here, when the user decides to submit the data he entered, I should have the server re-calculate and validate the total, and not rely on the result calculated on the client-side, as this could be tampered with by a malicious user.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Egypt Egypt
John has been working as a software developer since 2001. Development with Microsoft tools and technologies is his focus. He has good knowledge in designing and implementing windows and web applications for the .NET platform. He also worked extensively with MS SQL Server and MS Exchange Server.

Comments and Discussions

 
Questiononkeypress Pin
Timothy Vandeweerd2-Nov-07 10:35
Timothy Vandeweerd2-Nov-07 10:35 
Questiondatagrid please help me Pin
hi5siva12-Oct-06 1:54
hi5siva12-Oct-06 1:54 
AnswerRe: datagrid please help me Pin
John Wadie24-Oct-06 1:50
John Wadie24-Oct-06 1:50 
Generalwhy this is not built in VS.Net Pin
matro2-Aug-05 22:07
matro2-Aug-05 22:07 
GeneralGreat idea Pin
Mohamed Sharaf18-Jul-05 8:54
Mohamed Sharaf18-Jul-05 8:54 
GeneralRe: Great idea Pin
John Wadie26-Jul-05 4:30
John Wadie26-Jul-05 4:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.