Passing an array of values conforming to the schema of the data source to the add function adds data to the data source:
$find(<GRID_CLIENT_ID>).get_rows().add(<NEW_VALUES_ARRAY>);
To remove a row, the remove function is used:
$find(<GRID_CLIENT_ID>).get_rows().remove(<ROW_INSTANCE>,false);
Updates to data in the grid are persisted by wrapping the WebDataGrid in an UpdatePanel and initiating a post back to the server.
The WebDataGrid provides a rich API for conducting CRUD operations on the server, but many customers are not aware of the client-side API available for adding, modifying and removing rows from the data source. The key to executing these operations on the client is being aware of a few lines of JavaScript. This tutorial demonstrates how to insert, update and delete data in the grid entirely from the client.
Note: While I suggest that the CRUD operations are “entirely on the client” this label may be a bit misleading. All of the persistence is still conducted on the server, but a client-side CRUD approach endeavors to send persistence messages to the server using Ajax instead of requiring a full post back to the server. Further, the client-side CRUD method alleviates the need to write code behind in order to interact with the data source.
Consider a page that has a grid and a few other controls available to the user:
The WebDataGrid is responsible for displaying data in the standard manner. Deleting a row from the grid (and subsequently the data source) is a simple matter of clicking on the row selector and pressing the Delete button. Updates to the grid’s data are made in-line in the grid and are not instantly saved. The grid allows you to edit as many fields as you wish and changes are persisted by clicking the Update button. Finally, data is added to the data source by entering the required data and then clicking the Add button.
Note: The WebDataGrid features some default controls which provide an interface to add data to the grid. Using the blank row at the bottom of the grid will by default use an Ajax request to add data to the source, but the purpose of this article is to demonstrate how to use input controls that are disconnected from the grid to insert data.
Be sure to download the code here to step through the code on your own.
The markup for this sample includes a number of different controls which are discussed in sections. The first group of controls on the page are the WebDataGrid and UpdatePanel.
The markup for this sample begins with the traditional inclusion of the ScriptManager and the WebDataGrid. A small twist to this sample is the use of an UpdatePanel that surrounds the grid and a few button controls.
The WebDataGrid features a pay-to-play model where the scripts and other resources required by the grid for it’s rich features are only served to the client if the behaviors are enabled. Therefore examine the below screenshot of the behaviors editor to get an idea of the behaviors required to execute client CRUD.
Enabling Cell Editing, Row Adding and Row Deleting ensures the script files needed to enable these behaviors on the grid are served to the client. The Row Selectors behaviors is enabled to provide a place to the user to select an entire row, which is required to remove a row. Finally, the Selection behavior is needed to format how the row selection is conducted. In this case when a cell or the row selector is clicked, the entire row is selected. Selecting the whole row is necessary so the grid will fire the RowSelectionChanged event, which in this case is handled by the onRowSelectionChanged function. This function provides the hooks needed to programmatically locate selected row.
To see how all these settings translate into markup, here is the UpdatePanel and grid markup for the page:
<asp:UpdatePanel runat="server"> <ContentTemplate> <ig:WebDataGrid ID="wdg" runat="server" AutoGenerateColumns="False" DataSourceID="ods" DataKeyFields="Id" Width="400"> <Columns> <ig:BoundDataField DataFieldName="FirstName" Header-Text="First Name" Key="FirstName" /> <ig:BoundDataField DataFieldName="LastName" Header-Text="Last Name" Key="LastName" /> </Columns> <Behaviors> <ig:EditingCore> <Behaviors> <ig:CellEditing /> <ig:RowAdding /> <ig:RowDeleting /> </Behaviors> </ig:EditingCore> <ig:Selection CellClickAction="Row" RowSelectType="Single"> <SelectionClientEvents RowSelectionChanged="onRowSelectionChanged" /> </ig:Selection> <ig:RowSelectors> </ig:RowSelectors> </Behaviors> </ig:WebDataGrid> <input type="button" value="Delete" onclick="del();" /> <asp:Button Text="Update" runat="server" /> </ContentTemplate> </asp:UpdatePanel>
An ASP.NET data source control is required to provide an interface for the WebDataGrid to access the application’s persistence mechanisms. This sample uses the ObjectDataSource, but you may use any other data source control for your purposes.
The data source control points to the matching methods off a class named PersonRepository to delegate the work of persisting changes:
<asp:ObjectDataSource ID="ods" runat="server" DataObjectTypeName="Person" DeleteMethod="Delete" InsertMethod="Insert" SelectMethod="GetAll" TypeName="PersonRepository" UpdateMethod="Update" onobjectcreating="ods_ObjectCreating"> <DeleteParameters> <asp:Parameter Name="id" Type="Int32" /> </DeleteParameters> </asp:ObjectDataSource>
Notice the event handler for onobjectcreating. This event handler is used in order to provide the data source control with a single instance of the PersonRepository class, rather than a new instance being created upon each operation of the data source control. There are a few lines of code in the code behind in order to provide the control with it’s object instance:
using System; using System.Web.UI.WebControls; public partial class _Default : System.Web.UI.Page { private WebStateRepository<personrepository> _repository = new WebStateRepository<personrepository>(); protected void ods_ObjectCreating(object sender, ObjectDataSourceEventArgs e) { e.ObjectInstance = this._repository.Instance; } protected void Page_PreRender(object sender, EventArgs e) { if (this.Page.IsPostBack) { this._repository.Persist(); } } }
The important part of this code listing is in the body of the ods_ObjectCreating method. This is where you give the ObjectDataSource control the instance of the object you want to use.
Note: The WebStateRepository class is a utility class used in this sample to store the object collection in session state. In your implementation, you will simply point the data source control to an instance of your real repository class.
The listing below demonstrates the markup required to render the text boxes which give the user a place to enter the new person’s first and last name. When the Add button is clicked, a JavaScript function named add is run on the page.
<div> <input type="text" id="firstName" name="firstName" /> <input type="text" id="lastName" name="lastName" /> <input type="button" value="Add" onclick="add();" /> </div>
Up to this point all the code in this article should be relatively familiar to you. The last step required is to wire up the client-side messages to add and delete data. (Remember updates are sent to the server in a batch by changing data in-line in the grid and initiating a post back to the server inside an UpdatePanel)
function add() { var grid = $find("<%= wdg.ClientID %>"); var rows = grid.get_rows(); var newPerson = [$get("firstName").value, $get("lastName").value]; rows.add(newPerson); }
To send the insert request to the server all that is required is to pass an array of the new values to the add function off the rows collection. This function uses the ASP.NET Ajax selectors to find the firstName and lastName controls on the page and create a new array based on the values in the controls. To commit the insert, the array is passed to the add function.
Deleting data is a two step process. First the user must select which row is marked for deletion. Once the selected row is located, then the row may be removed. To handle the selection process the RowSelectionChanged event calls the onRowSelectionChanged function:
var selectedDataKey = ""; function onRowSelectionChanged(sender, eventArgs) { var rows = eventArgs.getSelectedRows(); var selectedRow = rows.getItem(0); selectedDataKey = selectedRow.get_dataKey(); }
First the selectedDataKey variable is created to store the selected key found when the row selection is changed. The onRowSelectionChanged function interrogates the eventArgs for the selected rows. Earlier the grid was configured to only allow single selection of rows, so the selectedRow variable gets its values safely by calling row.getItem(0). Finally the value for the selectedDataKey comes from calling the get_dataKey() function of the selectedRow instance.
Now that the selected key is a known value the key is used to commit a delete to the data source:
function del() { if (selectedDataKey != "") { var grid = $find("<%= wdg.ClientID %>"); var rows = grid.get_rows(); var row = rows.get_rowFromKey(selectedDataKey); rows.remove(row, false); } else { alert("Please select a row to remove."); } }
From an instance of the grid’s rows collection the get_rowFromKey method will find the single row selected earlier by the user. Passing the row instance to the row collection’s remove function sends a delete message to the server. The second argument of the remove method is a bit unintuitive. The argument name is noncommitting:
Therefore if you want to commit the change to the data source, you must pass in a false to the function.