Originally I was going to call this post, "Look Ma! No view state!" but view state isn't really what I to talk about it. This example will have no view state, but that's a side benefit.
What I want to talk about is client side data binding with jQuery. The
jQuery tmpl plugin has been documented in a number of places and I'm not going to show anything new here. This post is intended to be a simple example for people still using data grids and heavy .Net server controls. This example won't use any server controls. We won't have a
form tag and there will be no code behind. Let's get to it.
Step 1: Create a web service
Here's our simple web service. I called it "wsPlayers". It has one method and returns a list of custom objects based on a simple NBAPlayer class. Be sure to uncomment the line...
[System.Web.Script.Services.ScriptService]
...so that the web methods in the service can be called from client side JavaScript. We will also want to add a reference to the Script.Services namespace at the top of the file...
using System.Web.Script.Services;
...so that we can change the format of our web method to JSON (it's XML by default).
So here is the full text of our web service method:
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services;
namespace tmplDemo
{
[WebService(Namespace = "http://tmplDemo/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class wsContacts : System.Web.Services.WebService
{
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public List<NBAPlayer> GetPlayers(int paramMinChampionships)
{
List<NBAPlayer> tempPlayers=new List<NBAPlayer>();
NBAPlayer temp1 = new NBAPlayer("Nowitzki","Dirk",1);
NBAPlayer temp2 = new NBAPlayer("James", "LeBron", 0);
NBAPlayer temp3 = new NBAPlayer("Wade", "Dywane", 1);
NBAPlayer temp4 = new NBAPlayer("Bryant", "Kobe", 5);
NBAPlayer temp5 = new NBAPlayer("Paul", "Chris", 0);
tempPlayers.Add(temp1);
tempPlayers.Add(temp2);
tempPlayers.Add(temp3);
tempPlayers.Add(temp4);
tempPlayers.Add(temp5);
return (from p in tempPlayers
where p.championships>=paramMinChampionships
orderby p.lastname,p.firstname
select p).ToList<NBAPlayer>();
}
}
public class NBAPlayer{
public NBAPlayer(string lname,string fname,int chmps){
lastname=lname;
firstname=fname;
championships=chmps;
}
public NBAPlayer()
{
}
public string lastname { get; set; }
public string firstname { get; set; }
public int championships { get; set; }
}
}
As you can see from the code, the web service has a method called
GetPlayers and it accepts one parameter of type
int called
paramMinChampionships. It then returns a list of type NBAPlayer where each NBAPlayer object has a championship property equal to or greater than the
paramMinChampionships.
Now, I've made a custom class and hard coded a few elements for the example but this is where you'd put your database calls or Linq objects and build your return set that way. We're done with the web service.
Step 2: Download jQuery files.
Create a directory in your project called jQuery. Download these two jQuery files:
Add these files to the jQuery directory you made at the beginning of this step.
Step 3: Create a default.aspx page.
Add a new file called
default.aspx. Once you've done that, your solution explorer should look something like the image at left.
Now the fun part. First we're going to create some javascript that will do our Ajax call for us:
<script type="text/javascript">
function GetPlayers() {
$.ajax({
url: "wsPlayers.asmx/GetPlayers",
dataType: "json",
type: "POST",
contentType: "application/json; charset=utf-8",
data: ("{paramMinChampionships: " + $("#selChamps").val() + "}"),
error: function (err) {
alert("Error:" + err.responseText);
},
success: function (results) { OnComplete(results.d) }
});
}
function OnComplete(results) {
$("#tbodyPlayers").empty(); //We want to clear the body of the table first.
$("#playerDataTemplate").tmpl(results).appendTo("#tbodyPlayers");
}
</script>
If that doesn't make sense, then stop right here and read about the
jQuery Ajax method. What we're doing is calling our web service and passing it a numeric value so that it will give us back our list of players. Before we do that lets add the rest of the HTML to our default.aspx page.
We want a select element that looks like this:
Players with at least
<select id="selChamps">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
championships.
We'll want a button to fire off our ajax web service call:
<button id="btnSubmit" onclick="GetPlayers();" >Submit</button>
We'll also want a table to display our data:
<table border="1">
<thead>
<tr>
<td>First Name</td>
<td>Last Name</td>
<td>Championships</td>
<td>Bing</td>
</tr>
</thead>
<tbody id="tbodyPlayers">
</tbody>
</table>
Lastly, and most importantly, we add the data template:
<script id="playerDataTemplate" type="text/x-jquery-tmpl">
<tr>
<td>${firstname}</td>
<td>${lastname}</td>
{{if championships>0}}
<td align="center"><span style="color:green;font-weight:bold;">${championships}</span></td>
{{else}}
<td align="center">${championships}</td>
{{/if}}
<td><a target="_blank" href="http://www.bing.com/search?q=${firstname}+${lastname}">search</a></td>
</tr>
</script>
That's right, we're putting HTML markup directly inside our <script> tag in the example above. Notice that the
type is
type="text/x-jquery-tmpl" and not type="text/javascript". This is a different kind of script that works with the
tmpl plugin. The magic is in the
OnComplete method and the data template we created. Pay special attention to this line of JavaScript code:
$("#playerDataTemplate").tmpl(results).appendTo("#tbodyPlayers");
What this means is, we're going to take the value of results, which is the JSON object that contains our list of player data, pass it to the
tmpl function of the
playerDataTemplate selector and append the output to the
tbodyPlayers selector. The playerDataTemplate will apply what's inside for each element in the results variable. As you can see, by using the {} brackets we can drop the values of the properties whereever we want. We can apply conditional
if/else and we can put our property values inside HTML markup. And we can do it all without asp.net server controls or a code behind. Well,we do have a code behind of sorts but that is encapsulated in the web service. So if we have an iPhone app that wants to call our web service, no problem. Windows forms wants to use it? No problem. Another thing about this approach is that we are getting away from all the custom .net control styling markup and back to straight up css. Now when a designer looks at our source and wants to make some tweaks, they don't see a buch of <asp:textbox id="txtFirstName" runat="server" text="Johnny" />. The see regular HTML that they should be able to update without breaking anything.
Another advantage to this approach is that we are doing an Ajax call but since we're not using an asp.net update panel and things like that, we're only sending and receiving the JSON objects. That's a really light payload compared to all the view state stuff we'd get if we used update panels. We also aren't using an asp.net script manager, we're just referencing our jQuery libraries normally so we don't have that overhead either. I may do a follow up post to this one where I use update panels, script managers and a datagrid to show the difference in page size and network traffic. It makes a huge difference.
So, that's the
tmpl plugin for jQuery and that's why I love it. If you want to play around with this project yourself, you can download the whole thing
here.