Practical ASP.NET
Inserting with jQuery
Peter continues to extend his client-side case study by using jTemplate to extend a table to support inserts -- and then sending the user's data to the server to update the database.
This series started with the Visual Studio Magazine article
Integrating jQuery, Web Services, AJAX and ASP.NET (December, pg. 24), which integrated the ASP.NET ScriptManager and jQuery to implement client-side CRUD processing. Subsequently, over three columns (
Defining Templates with jTemplate,
Updating From the Client and
Deleting with jQuery and Web Services), I've extended the article's case study to do everything but insert new rows of orders -- which is what this column is all about.
Structuring the HTML and templates
To insert a new row into the page's HTML table, I need to generate a row using my jTemplate template. My first step is to restructure my existing template, which currently generates the whole table, to generate only a single tablerow:
{#foreach $T as ord}
‹tr›
‹td id="Delete"›‹input type="checkbox" id="DeleteFlag" /› ‹/td›
‹td id="OrderId"›{$T.ord.OrderId}‹/td›
‹td id="ShippedDate"›
‹input id="Data" type="text" value="{$T.ord.ShippedDate}" /›
‹/td›
...more cells for other Order data...
‹/tr›
{#/for}
The rest of my original table template now goes into my page where I use the table's style attribute to make the table invisible. I also assign an id to the table's tbody tag so that I can target it from jQuery:
‹table id="OrdersTable" style="display:none;"›
‹thead›
‹tr›
‹td width="20%"›Delete‹/td›
‹td›Order Id‹/td›
‹td›Shipped Date‹/td›
‹/tr›
‹/thead›
‹tbody id="OrdersTableExisting"›
‹/tbody›
‹/table›
With jQuery, displaying the table after loading it with Order data requires just this:
$("#OrdersTable").show();
I then added a div element to my page to act as a staging area when generating my new table row:
‹div style="display:none;" id="OrdersTableInsert"›‹/div›
Processing the template
I now added a new HTML button to my page and had it call the following function to generate a row and add it to the table. This code picks up the file holding my template and associates it with my staging area element. I then create an instance of my client-side object and set its properties to any defaults I have for a new order. I add that object to an array, pass it to the jTemplate processTemplate command, generate a row into my staging area, and append that new row to my table:
function DoAddRow()
{
$("#OrdersTableInsert").setTemplateURL("Templates/OrdersTable.htm");
var ordIList = new Array;
var ord = new NorthwindOrder();
ord.OrderId = "‹..›";
ordIList.push(ord);
$("#OrdersTableInsert").processTemplate(ordIList);
$("#OrdersTableExisting").
append($("#OrdersTableInsert")[0].innerHTML);
}
Processing Inserts
Eventually, the user will click a button and I'll process any inserted rows. First, I set up a variable to hold NorthwindOrder objects, an array to hold any objects I create, and retrieve the id of the Customer the user selected in the drop-down list on the page:
var ord;
var ordIList = new Array();
var CustId = $('#ctl00_MainPlaceHolder_ListBox1')[0].value;
I'll use my default OrderId ("‹..›") to find any inserted rows, but I don't want to retrieve the tablecells containing "‹..›" -- I want the tablerow with those cells. This jQuery finds all the tablerow elements in my OrdersTable and then filters that list down to just the rows with "‹..›" inside:
$("#OrdersTable tr").filter(":contains('‹..›')").each(function() {
In the function that jQuery calls for each element retrieved, I first create an instance of my NorthwindOrder object and then process every cell in the row (again, using jQuery). For each row, I retrieve the input element inside the cell (all of which have an id of "Data") and, based on the cell's id, set the appropriate property on the Order object. Finally, I set the CustId property using the value from the drop down list and add the object to my array:
ord = new NorthwindOrder();
$(this).find("td").each(function()
{
switch (this.id) {
case "ShippedDate":
ord.ShippedDate = $(this).find("#Data").val();
break;
...more properties set...
}
ord.CustId = CustId;
ordIList.push(ord);
After processing all the rows, I check to see if there are any objects in my array and, if there are, pass the array to a Web Service to do the database updates:
if (ordIList.length › 0)
{
NWCusts.InsertCustomerOrders(ordIList, OrdersInserted,
GenericFailure);
}
Inserting the List of Orders on the Server
In my Web service, I'm using Entity Framework and LINQ to do my database changes. In addition to creating the EF object that represents my Orders table, I also have to retrieve the related Customer object so I can add the Order to that Customer:
Dim entsNW As New northwndModel.northwndEntities
Dim NWOrder As northwndModel.Orders
For Each ord As NorthwindOrder In Orders
NWOrder = New northwndModel.Orders()
NWOrder.ShippedDate = ord.ShippedDate
...set other properties...
cust.Orders.Add(NWOrder)
Dim cust = (From custmr In entsNW.Customers _
Where custmr.CustomerID = ord.CustId _
Select custmr).First
entsNW.AddToOrders(NWOrder)
Next
entsNW.SaveChanges()
After one article and four columns, I've got a page with full CRUD functionality for both single and multiple records, combining AJAX for ASP.NET, WCF, jQuery, and jTemplate, Entity Framework and LINQ. The challenge, now, is for Visual Studio 2010 and .NET 4 to integrate these disparate technologies into a cohesive package for developers.
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/.