Practical ASP.NET
Updating From the Client with jQuery
Peter continues to exploit jQuery in his client-side code to integrate an ASP.NET page with a WCF service -- this time to support updating data.
In an earlier article
Integrating jQuery, Web Services, AJAX and ASP.NET, I integrated Web Services, jQuery, and ASP.NET to create a page that retrieved a single customer object and made it available for updating in a web page. Last week's column,
Defining Templates with jTemplate, extended the case study in the article to display multiple sales orders for the customer (see Figure 1). This week, I'll add updates.
Figure 1. A Client-side generated table -- The table of sales orders allows the user to update multiple rows and submit all the changes simultaneously by clicking on the Update button.
Begin on the Server
My first step, as usual, is to add a method to the WCF service I created in the original article. This method accepts an array of NorthwindOrder objects, finds each corresponding object in an Entity Framework model of the Northwind database, and updates the ShippedDate. Using Entity Framework and LINQ isn't essential, but it does shorten my code.
‹OperationContract()› _
Public Function UpdateCustomerOrders(ByVal Orders As NorthwindOrder()) _
As String
Try
Dim entsNW As New northwndModel.northwndEntities
For Each ord As NorthwindOrder In Orders
Dim Aord = (From ordr In entsNW.Orders _
Where ordr.OrderID = ord.OrderId _
Select ordr).First
Aord.ShippedDate = ord.ShippedDate
Next
entsNW.SaveChanges()
Return "OK"
Catch ex As Exception
Return ex.Message
End Try
End Function
On the Client
In last week's column, I created a jTemplate template to facilitate generating my table of sales order data. To support updating, I've enhanced that template by adding id attributes to the table, the cells in the data rows, and the textbox within the cell. I also enclosed the column headers and data rows in thead and tbody elements:
‹table id="OrdersTable"›
‹thead›
‹tr›
‹td›Order Id‹/tD›
‹td›Shipped Date‹/td›
‹/tr›
‹/thead›
‹tbody›
{#foreach $T as ord}
‹tr›
‹td id="OrderId"›
{$T.ord.OrderId}
‹/td›
‹td id="ShippedDate"›
‹input id="Data" type="text"
value="{$T.ord.ShippedDate}" /›
‹/td›
‹/tr›
{#/for}
‹/tbody›
‹/table›
When the user clicks on the Update button, I need to create a set of objects and pass them to my service. First I declare a variable to hold one Order, an array to hold all the orders to be passed back to the server, and a counter to keep track of where the next order should go in the array:
var ord;
var ordList = new Array();
var i = 0;
There's probably a dozen ways to build my array of orders but, since each row in the table represents a single order, it made sense to me to use jQuery to retrieve a collection of all of the rows. In this example, my jQuery selector finds all the tr elements (the final element mentioned), but only if the tr element is inside a tbody element and, furthermore, only if that tbody element is inside whatever element has the id "OrdersTable". The each function that I call will execute the enclosed function once for every row I find:
$("#OrdersTable tbody tr").each(function() {
...create array of NorthwindOrder objects...
});
Within the enclosed function, I first create a NorthwindOrder object and set my ord variable to point to it. At this point the reference this will be pointing to the tr element currently being processed. I can use $(this) and jQuery's find function to search within the current tr element to retrieve all the td elements. When I finish this processing, I'll have set the properties on my NorthwindOrder object so I'll add it to the array that will be sent to my Web Service:
ord = new NorthwindOrder();
$(this).find("td").each(function() {
...set properties on ord...
});
ordList[i] = ord;
i++;
To set the properties on the NorthwindOrder object, I first check the id property of the td element. If it's the OrderId cell, I can just extract its innerText. For the ShippedDate cell, however, I need to do another find to retrieve the input element that contains the user's data. jQuery helps here again (as does assigning an id value of "Data" to the input element):
switch (this.id) {
case "OrderId":
ord.OrderId = this.innerText;
break;
case "ShippedDate":
ord.ShippedDate = $(this).find("#Data").val();
break;
}
Because I've already added a reference to my Web Service to the ScriptManager on my site's Master Page, I can just call my new UpdateCustomerOrders method from my client-side code:
NWCusts.UpdateCustomerOrders(ordList, OrdersUpdated, GenericFailure);
I still need to handle inserts and deletes, but you'll have to come back next week for those.
About the Author
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.