Practical ASP.NET

Setting Up for jQuery Templates

Peter Vogel prepares to try out the new jQuery templating/databinding functionality to create a simple AJAX-enabled page that allows the user to select a customer and retrieve from a Web service all the orders associated with that customer.

Starting with my next column, I'm going to focus on the new databinding and templating features that Microsoft has contributed to the jQuery project. In this column I'm going to build the base project that I'll be integrating the new jQuery features into.

I have three goals in this project:

  • Offload as much processing as possible to the client
  • Have the client talk to the server exclusively through JavaScript calling WCF Web services
  • Leverage as much ASP.NET technology as possible

The page I'm going to create is very simple: The user will get a list of customers from which to select (that list will be loaded on the server). Once the user selects a customer, a list of existing orders for that customer will be displayed. The user will then be able to update and delete orders. While I could implement adding new orders (as I did in earlier projects) it wouldn't leverage the jQuery extensions, so I'm going to skip that.

As my first steps, I set up an ASP.NET application, copy the Northwind database into the application's App_Data folder, and create an Entity Framework model based on the Northwind database containing Customer, Order, OrderDetail and Product entities.

I also define a Data Transfer Object, for moving data between the client and server. The following code shows one of the properties on the DTO, RequiredDate, which I move between the client and the server as a string (the JSON format for transferring data in AJAX doesn't understand the concept of "DateTime" and, while WCF has a hack for dealing with dates, it's easier just to convert my dates to strings). The KnownType attribute ensures that code in my project can serialize and deserialize my DTO:

<System.Runtime.Serialization.KnownType(GetType(dtoOrder))> _
Public Class dtoOrder

    Private _RequiredDate As String
    Public Property RequiredDate() As String
        Get
            Return _RequiredDate
        End Get
        Set(ByVal value As String)
            _RequiredDate = value
        End Set
    End Property
...  more properties... 

With my business layer created, I add my Northwind data factory: an AJAX-enabled WCF service called NorthwindData. The first method I add to the service uses LINQ to return a list of my DTOs (one for each order for the customer id passed to the method) with a re-formatted date:

<ServiceContract(Namespace:="PHVIS")> _
<AspNetCompatibilityRequirements(
  RequirementsMode:=AspNetCompatibilityRequirementsMode.Allowed)> _
Public Class NorthwindData

<OperationContract()> _
Public Function GetOrdersForCustomer(ByVal CustId As String) _
As List(Of dtoOrder)
Dim nw As New northwndEntities
Dim res As New List(Of dtoOrder)
Dim dto As dtoOrder

  Dim ords = From o As Orders In nw.Orders.Include("Customers") _
           Where (o.Customers.CustomerID = CustId) _
           Select o

  For Each o As Orders In ords
    dto = New dtoOrder With {
     .CustomerId = o.Customers.CustomerID, _
     .CompanyName = o.Customers.CompanyName, _
     .RequiredDate = Format(o.RequiredDate, "MM/dd/yyyy"), _
     .OrderId = o.OrderID}
     res.Add(dto)
  Next

  Return res
End Function
End Class

Turning to my ASP.NET. page, I add a dropdownlist called lstCustomers to display the list of customers and tie it to an EntityDataSource. That EntityDataSource retrieves all the Customer entities from my EF model, displaying just the CompanyName but holding the CustomerId in the list's data property. I also add a "Select Customer" item to the top of the list.

While I created and configured these components in Design view, here's what the results look like in Source view:

<asp:DropDownList ID="lstCustomers" runat="server" 
            DataSourceID="CustomerDataSource"
      DataTextField="CompanyName" 
            DataValueField="CustomerID" 
Height="24px" Width="193px" 
            AppendDataBoundItems="True">
            <asp:ListItem>Select Customer</asp:ListItem>
</asp:DropDownList>

<asp:EntityDataSource ID="CustomerDataSource" runat="server" 
            ConnectionString="name=northwndEntities" 
            DefaultContainerName="northwndEntities"
EntitySetName="Customers" 
            Select="it.[CompanyName], it.[CustomerID]">
</asp:EntityDataSource>

I drag a ScriptManager on to the page and add a reference to my Web service to its Services collection. Rather than host the jQuery files in my application, I also add, to the ScriptManager's Scripts collection, references to the jQuery core library and the new templating extension library in Microsoft's Content Delivery Network. Finally, I add a reference to the script library containing the scripts for my page.

Again, I made all of these changes in Design view and in the ScriptManager's Properties list, but the result looks like this in source view:

<asp:scriptmanager runat="server" ID="Scriptmanager1">
  <Scripts>
    <asp:ScriptReference Path=
      "http://ajax.microsoft.com/ajax/jquery/jquery-1.4.4.min.js" />
    <asp:ScriptReference Path= 
      "http://ajax.microsoft.com/ajax/jquery.templates/beta1/
                     jquery.tmpl.min.js" />
    <asp:ScriptReference Path="~/Scripts/DisplayOrders.js" />
  </Scripts>
  <Services>
    <asp:ServiceReference Path="~/NorthwindData.svc" />
  </Services>
</asp:scriptmanager>

And now, it's time to add some JavaScript. Once the page is loaded, I want to run a JavaScript function I've called InitializePage, where I link events fired by HTML elements on my page to my JavaScript functions. Using jQuery that code looks like this:

$(InitializePae);

At this point in the project, the only event I want to wire up is the dropdownlist's change event. The jQuery code to attach a JavaScript function called GetOrders to the lstCustomers' change event looks like this:

function InitializePage() {
    $("#lstCustomers").change(GetOrders);
}

I'm ready now to call my Web service in my GetOrders function. That function will retrieve the current value from lstCustomers and pass that to the GetOrdersForCustomer method in my Web service. When the results come back, I'll call another function named GotOrders that will catch the service's results (if anything goes wrong, I'll call a function named FailOrders that catch the error). Thanks to the ScriptManager, that code looks like this:

function GetOrders() {
    var custId = $("#lstCustomers").val();
    var nwData = new PHVIS.NorthwindData();
    nwData.GetOrdersForCustomer(custId, GotOrders, FailOrders);
}

function GotOrders(ords) {
    alert(ords.length);
}

function FailOrders(res) {
    alert(res._message);
}

And, in my next column, I'll use the new templating functions to display those objects.

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/.

comments powered by Disqus

Featured

Subscribe on YouTube