This was selected as an ASP.NET Article of the Day for September 29, 2009
This is a quick follow up to the previous post about calling ASP.NET PageMethods in which we used a ScriptManager object with EnablePageMethods set to True.
This time, we won’t even be including a ScriptManager in our page, and instead will use jQuery. Without further ado, here’s what the javascript looks like:
$(function() {
$("#ZipCodeTextBox").bind('change', function(event) {
// If it doesn't look like a zip code, don't even bother with the request
if (/^\d{5}(-\d{4})?$/.test($(this).val()))
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "Default.aspx/GetCityStateByZip",
data: "{'zip': '" + $(this).val() + "'}",
dataType: "json",
success: function(msg) {
$("#CityStateLabel").text(msg.d.City + ", " + msg.d.State);
}
});
});
});
If you’re familiar with jQuery, then this should look pretty familiar. If not, you might be surprised how much this small snippet is accomplishing.
First, there was no need to add an onchange attribute to our asp:TextBox like we did in the previous post. The practice of keeping our HTML markup clear from javascript calls for event handlers and such is known as ‘unobtrusive javascript.’ Personally, I think it’s kinda silly, but will admit that the HTML does look cleaner without being cluttered with extraneous attributes and bits of javascript code. The reason we don’t have to add the onchange attribute is because we are using jQuery to bind our text box to an onchange event handler:
$(function() {
$("#ZipCodeTextBox").bind('change', function(event) {
First, jQuery waits until the DOM (the page’s logical structure) is loaded so that we can be sure to find our text box we want to bind to. In this case, the ID attribute of the text box is ZipCodeTextBox. Using jQuery’s super-easy element selecting capability, we pass the CSS selector #ZipCodeTextBox to the $() function ($() is shorthand for a call to jQuery). We then call bind() on the text box, passing ‘change’ as the event type and as a second parameter, the function we want to execute in response to the firing of the change event. The function takes a parameter, event in this example, but we won’t be using it here.
Once the onchange event fires for our text box, the block of code inside the function will execute. First, we are going to doing a simple inspection of the value of the text box using a little regular expression. To actually retrieve the value of the text box, we can use $(this), which essentially enhances our text box element, giving it access to all pertinent jQuery features. In this case, $(this).val() is all we need – val() returns the value contained within form elements.
Our regular expression
/^\d{5}(-\d{4})?$/
is used to test the value to make sure that it consists of 5 digits – \d{5} – followed by an optional hyphen and four more digits – (-\d{4})?. Obvious examples of valid zip codes would be 01721-8582 or 95060. If the value entered in the text box doesn’t fit this format, we won’t even bother making a call to our PageMethod.
Putting it all together, we end up with:
/^\d{5}(-\d{4})?$/.test($(this).val())
From here, we call $.ajax(), another jQuery function, which makes an AJAX request using the parameters we specify. ASP.NET requires our request to be POSTed to the server (GET won’t work) as well as have its content type set to ‘application/json.’ The url parameter in our AJAX call is simply the name of the page cotaining the PageMethod (Default.aspx in our sample) followed by a forward slash and the name of the WebMethod in our page’s code-behind file. Our method must be decorated with the WebMethod() attribute in order to respond correctly to the request:
Imports System.Web.Services ... <WebMethod()> _ Public Shared Function GetCityStateByZip(zip As String) As CityState ... End Function
The data that we POST to the page needs to be in JSON format, which boils down to a set of name-value pairs contained within curly braces. Since our PageMethod is expecting ‘zip’ as a parameter, we construct our JSON data to look like {‘zip’: ’95064′}. We then set the data type of the request to ‘json’. The AJAX parameter list to this point:
type: "POST",
contentType: "application/json; charset=utf-8",
url: "Default.aspx/GetCityStateByZip",
data: "{'zip': '" + $(this).val() + "'}",
dataType: "json"
Finally, we define the ‘success’ parameter to our AJAX call, setting its value to a function whose parameter is the result of our request.
success: function(msg) {
$("#CityStateLabel").text(msg.d.City + ", " + msg.d.State);
}
We can do whatever manipulation of the data we need here, though some would argue that data processing should take place on the server-side. I guess it’s really dependent on the task at hand. At any rate, we are going to do a bit of DOM manipulation here, involving the simple assignment of a pre-defined asp:Label element’s text value to the City and State returned to us from the PageMethod. Again, $() takes a CSS selector as a parameter, returning to us the element we want to modify. And we just want to change the text of the label, so we use the text() function, passing to it the value we’d like set.
Initially, I expected that using msg.City and msg.State would work in the same manner as it did in our Microsoft AJAX callll to the page method. Was rather confusing until I was able to inspect the JSON returned from the PageMethod by using FireBug for Firefox (which if you don’t already have, you need to get right away http://getfirebug.com/). Seems ASP.NET wraps the entire JSON response and assigns it to a key simply named ‘d’. Our JSON response object:
{'d': {'City': 'Santa Cruz', 'State': 'CA'}}
As our JSON object is the parameter msg passed to the success function, to get the City and State values we must include the d in our expression – msg.City will not work, msg.d.City will.
So that’s about it! We managed to call our PageMethod with jQuery rather than the Microsoft AJAX library. I had been thinking that this might even be a suitable application for being moved entirely to the client-side. But then I thought that including an Yahoo Maps Application ID for anyone to view might not be the greatest idea. I think next time I will try and make a simple PHP “web service” to respond to our jQuery AJAX request, taking ASP.NET entirely out of the picture.
UPDATE: Per some requests, I’ve authored a simple demonstration, the source code of which can be found at: Source Code for Calling a PageMethod with jQuery