23 Dec 2011

Using jQuery with Client-Side Data Binding Templates


A few weeks back I posted about a JavaScript data binding template solution that I’ve been using that makes it easy to bind JSON data to a client-side template without having to write a lot of JavaScript code.  One of the people that commented on that post asked if I could put together a sample that demonstrated the templates in action.  It took me a few weeks to get to it, but I finally made some time to put a sample together that demonstrates the fundamentals of using the client-side templates and binding JSON data to them.  As a review (in case you didn’t read my earlier post), the template solution I’ve been using recently on a client project is based on some code written by John Resig (creator of jQuery) which is extremely compact.  Here’s a tweaked version of his original code that I wrapped with a jQuery extender function (credit goes to Rick Strahl as well for a few tweaks he added):
$.fn.parseTemplate = function(data)
{
    var str = (this).html();
    var _tmplCache = {}
    var err = "";
    try
    {
        var func = _tmplCache[str];
        if (!func)
        {
            var strFunc =
            "var p=[],print=function(){p.push.apply(p,arguments);};" +
                        "with(obj){p.push('" +
            str.replace(/[\r\t\n]/g, " ")
               .replace(/'(?=[^#]*#>)/g, "\t")
               .split("'").join("\\'")
               .split("\t").join("'")
               .replace(/<#=(.+?)#>/g, "',$1,'")
               .split("<#").join("');")
               .split("#>").join("p.push('")
               + "');}return p.join('');";

            //alert(strFunc);
            func = new Function("obj", strFunc);
            _tmplCache[str] = func;
        }
        return func(data);
    } catch (e) { err = e.message; }
    return "< # ERROR: " + err.toString() + " # >";
}

The parseTemplate method can be applied against a client-side template like the one below.  Notice that the template is wrapped in a script block with the type set to text/html so that it isn’t rendered by the browser.  JSON properties are written out by using the <#= …  #> syntax and the template engine has full support for embedded JavaScript code. 
<script id="MyTemplate" type="text/html">
    <table style="width:400px;">
        <thead>
            <tr>
                <th>First Name</th>
                <th>Last Name</th>
                <th>Address</th>
            </tr>
        </thead>
        <tbody>
        <#
            for(var i=0; i < d.length; i++)     
            {      
               var cust = d[i]; 
        #>
                <tr>
                   <td id="CustomerRow_<#= i.toString() #>">
                        <#= cust.FirstName #> 
                   </td>
                   <td>
                        <#= cust.LastName #>
                   </td>
                   <td>
                      <#= cust.Address.Street #>
                      <br />
                      <#= cust.Address.City #>, <#= cust.Address.State #>
                      &nbsp;&nbsp;<#= cust.Address.Zip #>
                   </td>
                </tr>
        <# 
            }
        #>
        </tbody>
    </table>
    <br />
    <#= d.length #> records shown
</script>

This template outputs a simple table like the one shown next.  Sure, I could’ve generated the table using DOM manipulation techniques, but being able to tweak a data template is much easier and productive in my opinion.
image

In order to use the template you’ll need to have some JSON data available.  Here’s an example of creating JSON by hand and binding it to the template using the parseTemplate method shown earlier.  The data returned from the template data binding operation is passed to the html method of the target div which displays the data in the browser.  Note:  I’m defining the “d” property in the JSON object since WCF uses that name by default when it returns serialized JSON data. 
var json =
        {
            "d":
            [
               { "FirstName": "John", "LastName": "Doe", 
                 "Address":
                  { "Street": "1234 Anywhere St.", "City": "Phoenix", 
                    "State": "AZ", "Zip": 85044 }
               },
               { "FirstName": "Jane", "LastName": "Doe",
                   "Address":
                  { "Street": "435 Main St.", "City": "Tempe", 
                    "State": "AZ", "Zip": 85245 }
               },
               { "FirstName": "Johnny", "LastName": "Doe", 
                 "Address":
                  { "Street": "643 Chandler Blvd", "City": "Chandler", 
                    "State": "AZ", "Zip": 85248 }
              },
               { "FirstName": "Dave", "LastName": "Doe",
                 "Address":
                  { "Street": "18765 Cloud St.", "City": "Mesa", 
                    "State": "AZ", "Zip": 85669 }
               }
            ]
        };
var output = $('#MyTemplate').parseTemplate(json);
$('#MyTemplateOutput').html(output);

Of course, in the real-world you’ll probably get the JSON data from some type of service (WCF, ASMX, REST, etc.).  Here’s a WCF service that returns a List of Customer objects and converts them to JSON.  The service has the client script behavior enabled so that serialization from CLR objects to JSON objects occurs behind the scenes automatically. 
[ServiceContract(Namespace = "http://www.thewahlingroup.com")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class CustomerService
{
    [OperationContract]
    public List<Customer> GetCustomers()
    {
        return new List<Customer>
        {
            new Customer {FirstName="John",LastName="Doe", 
                          Address=new Address{Street="1234 Anywhere St.",City="Phoenix",State="AZ", Zip=85044}},
            new Customer {FirstName="Jane",LastName="Doe", 
                          Address=new Address{Street="435 Main St.",City="Tempe",State="AZ", Zip=85245}},
            new Customer {FirstName="Johnny",LastName="Doe", 
                          Address=new Address{Street="643 Chandler Blvd",City="Chandler",State="AZ", Zip=85248}},
            new Customer {FirstName="Dave",LastName="Doe", 
                          Address=new Address{Street="18765 Cloud St.",City="Mesa",State="AZ", Zip=85669}}
        };
    }

}


jQuery’s ajax method can then be used to call the WCF service and retrieve the data (jQuery provides other methods such as getJSON that could be used too if desired): 
$.ajax(
{
    type: "POST",
    url: "CustomerService.svc/GetCustomers",
    dataType: "json",
    data: {},
    contentType: "application/json; charset=utf-8",
    success: function(json)
    {
        var output = $('#MyTemplate').parseTemplate(json);
        $('#MyTemplateOutput').html(output);

        //Add hover capabilities
        $('tbody > tr').bind('mouseenter mouseleave', function()
        {
            $(this).toggleClass('hover');
        });
    }
});

This code defines the type of operation, service URL to call, any data passed to the service, the content type and a success callback.  Once the service call returns, the JSON data is bound to the template shown earlier by locating the area where the template should be rendered to (MyTemplateOutput in this example) and then calling parseTemplate.  Hover capabilities are also added using jQuery’s bind method to highlight rows as the user moves the mouse in and out of them. 
You can see that the amount of custom Javascript that has to be written is kept to a minimum by combining jQuery with the client-side template which ultimately leads to easier maintenance down the road.  This is just one of several different client-side template solutions out there.  ASP.NET 4.0 will also include a custom client-side template solution as well as mentioned in my previous post. 

Download the sample code here

2 comments:

  1. Thanks PC Patel..You are great @Copy Pasting articals, I like this, You have introduced this nice artical by posting here..!!

    ReplyDelete
  2. its just about sharing good things with others...

    ReplyDelete