Find this article useful?
Printer Friendly View

Using jQuery with ASP.NET
Part 2: Making Ajax Callbacks to the Server

by Rick Strahl
www.west-wind.com/weblog

 

In the first part of this article series I introduced jQuery’s functionality and how it provides a rich client side programming model. This time around I’ll expand on these concepts and show you how you can use jQuery in combination with ASP.NET using it as an AJAX backend to retrieve data. I’ll also discuss how you can create ASP.NET controls and otherwise interact with jQuery content from ASP.NET pages in WebForms.

 

jQuery is just a JavaScript library so it will work seamlessly with ASP.NET both from page code as well as through backend driven code using the Page.ClientScript object or ScriptManager. You can use jQuery on its own as a client side and Ajax library that communicates with ASP.NET or you can use jQuery in combination with ASP.NET AJAX. The two actually complement each other quite well as jQuery provides functionality that the ASP.NET AJAX library does not and vice versa. For the most part the interaction between the two libraries is trouble free except for a few very rare edge cases.

 

In this article I’m not going to be talking much about ASP.NET AJAX since that’s been covered ad finitum in other places – the procedure doesn’t vary much if you’re using it with jQuery. Instead I’ll focus on using only jQuery plus a few small helpers to make callbacks to the server easily. In the process you’ll get to see how some of jQuery’s AJAX features work and how to manage the data coming back from the server in a few different ways.

First Ajax Steps with jQuery

One of the most obvious client side features of any Javascript client library is the ability to make AJAX calls to the server. jQuery includes a host of Ajax functions that make it easy to retrieve content from a Url starting with the low level and do-everything $.ajax() function  plus a number of others that are simpler and more focused to specific tasks. Here’s a list of some of the functions available:

 

$.ajax(opt)
This the low level Ajax function with many, many options that lets you create just about any kind of Ajax request. If you need full control over requests or you want to create a generic component that calls back to the server (like the WCF/ASMX client proxy I’ll discuss later) you’ll want to use this function. For now check out the documentation on the multitude of options available.

 

$(sel).load(url,data,callback)

The .load() function is the only Ajax function that works off a jQuery selector. It calls a URL on the server and loads the result as content into selected element(s). It’s a very quick and easy way to load Html fragments and inject them into the document if your result is HTML. An optional callback can be provided to be notified with the server result text when the callback completes which is useful if you want to visually adjust the retrieved content – like applying an effect to visually cue the user to an update. Note this function is heavily overloaded: If no URL is specified .load() acts as a load event handler that fires when an element has loaded its data (ie. an image or script).

 

$.get(url,callback),$.post(url,data,callback)
These functions are simple helpers that provide basic get and post operations to the server. You specify a URL and a callback which is called with the HTTP response from the server. $.post() also allows you to pass either formatted POST buffer string or an object the properties of which are turned into POST encoded key value pairs.

 

$.getJSON(url,data,callback)

Similar to $.post(), but expects the result to be JSON which is automatically deserialized into a Javascript value and passed to the callback as a parameter. While this function is handy for simple JSON results there are two things to watch out for: Dates are not parsed since there’s no date literal in Javascript, so whatever the server returns will be used (typically a string). $.getJSON() also doesn’t support JSON POST data – only POST encoded variables. This function is useful for simple JSON results returned from arbitrary services, but not usable for calling WCF or ASMX ASP.NET services since they expect JSON POST input. More on this later in the article.

.getJSON() also supports cross domain JSONP callbacks. If you specify a query string parameter of callback=? you can force the result to be routed to the callback you specify in the parameter list.

 

$.getScript(url,callback)

This function loads script code from the server and executes it once downloaded if no callback is specified. If specified the optional handler is fired instead and passed the Javascript, plus the current ajax request. This can be useful for JSONP cross domain callbacks where you have no control over the parameters used.

 

Global Ajax Events

There also a number of global Ajax events that you can take advantage of all of which take callbacks as parameters: ajaxCallback(), ajaxError(), ajaxSend(), ajaxStart(),ajaxStop(),ajaxSuccess(). These are useful for setting up global handlers that can centrally manage Ajax requests. You’re not likely to need these much unless you build components that need to know status of requests.


 

Getting started with $().load()

The load() function is the easiest way to retrieve content from the server and display it. Let’s look at a simple stock quote example from the included code samples. It retrieves a stock quote from Yahoo and displays the results using the .load() as shown in Figure 1.

Figure 1: A simple example loading a stock quote as HTML with the .load() function.

 

This page works by utilizing two HTML pages: One to hold the input form (StockAjaxLoad.aspx) and one to display the actual stock chart (StockDisplay.aspx). The StockDisplay form is a standalone server page that renders an HTML fragment and is one that is called from jQuery to return the fragment with the completed stock data.

 

The stock display form can be accessed simply via query string parameters by providing a symbol name, or providing an action=Chart key value pair to renders the stock chart image. The two Urls the form accepts look like this:

 

StockDisplay.aspx?Symbol=msft

StockDisplay.aspx?Symbol=msft&action=Chart

 

The form itself is a plain server form with a few label controls. The codebehind loads up a stock quote by using a StockServer class that goes out to Yahoo from the server and retrieves stock data, parsing it into a stock quote object. The resulting quote data is then assigned to the label fields. The page is not a full HTML document but rather just a fragment that starts with a <div> tag and <table> -  there’s no <html> or <body> tag since the output will be merged into the existing document. Check out and test the StockDisplay.aspx to see how the display and stock retrieval works.

 

The key part of the client page contains only the form fields and a placeholder is very simple and looks like this:

 

<div class="containercontent">

    Stock Symbol:

    <asp:TextBox runat="server" ID="txtSymbol" Text="MSFT" />       

    <input type="button" id="btnSubmit" value="Get Quote"   

           onclick="showStockQuote();" />

    <img src="../images/loading_small.gif" id="imgLoading"
         style="display: none"/>

    <hr />

    <div id="divResult" style="width:420px"></div>

</div>

 

To load a stock quote with jQuery we can now very easily read the symbol and use the .load() function to load the result into divResult.

 

<script type="text/javascript">

    function showStockQuote() {                       

        $("#divResult").load("StockDisplay.aspx",

                             { symbol: $("#txtSymbol" ).val() });  

                      

    }

</script>

 

That’s it! We call .load() with the URL plus the optional data parameter which can be either a string (a raw POST buffer) or an object map, the properties of which turn into POST parameters. Here I’m passing an object with a single symbol property that is turned into a POST var.

 

On the server form the StockDisplay.aspx fragment page code simply picks up the symbol, retrieves the stock quote and updates the HTML display and renders the page. Listing 1 shows the StockDisplay codebehind that handles both the stock display and stock chart image.

 

Listing 1: The StockDisplay codebehind loads a quote and displays it

public partial class StockDisplay : System.Web.UI.Page

{

 

    protected void Page_Load(object sender, EventArgs e)

    {

        string action = Request.Params["Action"];

        if (action == "Chart")

            this.GetStockChart();

       

        this.GetStockQuote();

    }

 

 

    void GetStockQuote()

    {

        string symbol = Request.Params["Symbol"] ?? "";

 

        StockServer stockServer = new StockServer();

        StockQuote quote = stockServer.GetStockQuote(symbol);

 

        this.lblCompany.Text = quote.Company + " - " + quote.Symbol;

        this.lblPrice.Text = quote.LastPrice.ToString();

        this.lblOpenPrice.Text = quote.OpenPrice.ToString();

        this.lblNetChange.Text = quote.NetChange.ToString();           

        this.lblQuoteTime.Text = quote.LastQuoteTimeString;

 

        // *** this will call this page to retrieve the chart     

        this.imgStockQuoteGraph.ImageUrl = "StockDisplay.aspx?Symbol=" +

                                           quote.Symbol + "&action=Chart";

    }

 

    void GetStockChart()

    {

        string symbol = Request.Params["Symbol"] ?? "";

 

        StockServer stockServer = new StockServer();

        byte[] img = stockServer.GetStockHistoryGraph(

                        new string[1] { symbol },

                        "Stock History for " + symbol,

                        400, 250, 2);

        Response.ContentType = "application/jpg";

        Response.BinaryWrite(img);

        Response.End();

    }

 

}

 

Note that the single page handles both the text and image displays by routing via query string and post variables and a little routing.

 

That was easy. Using .load() to call an external page is childishly simple.

 

But we probably should improve the sample a little bit. The first thing to do is to make the display a little more interactive. When you load a stock quote when one is already active it’d be nice to use an effect to ‘clear’ the old quote and display the new one. We can use the .load() method and its callback parameter to coordinate sliding the display up and then down again once the quote has loaded completely. This takes a little coordination as Listing 2 demonstrates.

Listing 2: Indication progress in the stock display with basic animation

function showStockQuote() {

    var div = $("#divResult");

    showProgress();

    div.slideUp(function() {

            div.load("StockDisplay.aspx",

                     { symbol: $("#txtSymbol").val() },

                     function() {

                         $(this).slideDown();

                         showProgress(true);

                     });

        });                                                 

}

function showProgress(hide) {

    var img = $("#imgLoading");

    if (hide)

        img.hide();

    else

        img.show();

}

ASP.NET ClientIds and jQuery Selectors

In the code above I’m simply using the ID of the txtSymbol control and that works fine in this particular example, because the txtSymbol control is not contained inside of any ASP.NET naming container like a MasterPage or UserControl. If it did the code above would fail because the ID would not be found.

 

Specifically inside of a master page you might find that the ID gets mangled by ASP.NET into: ctl00_Content_txtSymbol. I could change my code to read:

{ symbol: $("#ctl00_Content_txtSymbol").val() }

 

which works, but is pretty ugly and volatile because the parent IDs might change if a container is renamed or moved around.

 

Another option is to use a little server side script markup to embed the ClientID:


{ symbol: $("#<%= txtSymbol.ClientID %>").val() }


This is also ugly, but reliable. But this does not work if you end up moving your code into a separate .js script file. If you use client ids like this a lot you might create a list of them as global variables:

 

var txtSymbolId = "<%= txtSymbol.ClientID %>";

which then lets you reuse the variable a little more easily:

 

{ symbol: $("#" + txtSymbolId).val() }

 

These variables are also visible in loaded script files.


Finally, some time ago I created a ScriptVariables component that allows for creating variables on the server and rendering them into the client. Among its features this component can expose all control client IDs as object properties under a named object. You can find out more about this component in this blog post. Using this approach you’d end up with a server variable of your choosing that can be referenced like this:

 

{ symbol: $("#" + serverVars.txtSymbolId).val() }


All of this is pretty ugly and might make you want to consider using master pages and user controls in script heavy pages, but the workarounds are fairly easy, just tedious.

 

In the future ASP.NET 4.0 will bring some relief in this regard with a new Control property that lets you specify how control IDs are rendered with an option to override INamingContainer name mangling.

Using .load() with the same ASPX page

In the last stock example I used a separate ASPX page to display a result, which is great if what you’re rendering warrants a self contained page that you might reuse. But often you simply want to render some HTML to load into the page by simply calling back to the current page to keep things self contained. Having a separate page (or even a user control) for each AJAX callback certainly can be overkill.

 

However, you can use pretty much the same process used in the last example to call back to the same page. The main difference is that you’ll need to do a little bit of routing to determine whether you are in a callback or whether you’re rendering the full page as a standard postback.

 

Let’s look at another example that demonstrates this Page callback process. In this example, I’ll use some server side control rendering to render a ListView Control with data and return the Html fragment result back to the client. Again the client page uses $(sel)..load(). Figure 2 shows what the sample looks like.

 

Figure 2: A server side control’s HTML loaded into the client page via .load()

 

The layout of this portion of the sample is very simple. It contains merely the dropdown list plus a placeholder that receives the result from the server.

 

<fieldset>   

<legend>.load() with ASP.NET Control</legend>

<div class="fieldsetpadding">   

    Show Entries:

    <asp:DropDownList runat="server" id="lstFilter"
                      onchange="showEntries()">

        <asp:ListItem Text="Recent Items" Value="Recent" />

        <asp:ListItem Text="All Items" Value="All" />

        <asp:ListItem Text="Open Items" Value="Open" />    </asp:DropDownList>

   

    <a href="javascript:{}" id="lnkShowEntries">Go</a>

   

    <div id="divEntryDisplay" style="margin: 10px;display:none;">
    </
div>   

</div>   

</fieldset>

The page also contains a non-visible ListView control and some containing markup that is not initially rendered:

 

<!-- EntryList 'Template' rendered for AJAX retrieval -->

<asp:PlaceHolder runat="server" ID="entriesPlaceHolder" Visible="false">

   

<div class="blackborder" style="width:500px;">       

    <div class="gridheader">Select one of the Open Entries</div>

    <div style="height: 300px;  overflow-Y: scroll; overflow-x: hidden;">

 

    <asp:ListView runat="server"  ItemPlaceholderID="layoutContainer" ID="lstEntries">   

    <LayoutTemplate>

            <div id="layoutContainer" runat="server" />           

    </LayoutTemplate>

    <ItemTemplate>

    <div id="itemtemplate"

         onclick="alert('Clicked Entry: ' + $(this).attr('pk')); return false;"

         pk="<%# Eval("pk") %>">       

        <div id="clockimg"></div>

        <b><a href="javascript: alert('clicked');"><%# Eval("Title") %></a></b><br />

        <small><%# Eval("Timein") %></small>   

    </div>

    </ItemTemplate>

    </asp:ListView>

    </div>

    <div id="divListStatus" class="toolbarcontainer" >

        <asp:Label runat="server" ID="lblListCount"></asp:Label>

    </div>

</div>

 

</asp:PlaceHolder>

 

When the selection changes in the list, the showEntries Javascript function runs and calls the server to retrieve the list based on the value selected in the filter drop down:

function showEntries()

{           

    var filter = $("#" + scriptVars.lstFilterId).val();

    var jDiv = $("#divEntryDisplay");

   

    jDiv.load("simplepageCallbacks.aspx?Callback=EntryList",

                               { Filter: filter },

                               function(result,status,xhr) {

                                 jDiv.slideDown(1000);  

                                 $("#lnkShowEntries").text("hide");

                               });

}   

 

On the server side the key to making page callbacks into the same page is routing. Notice the URL I’m using above which includes a Callback=EntryList query string parameter. This sample page contains several different callbacks and each of them has a unique Callback id that I’ll use on the server side to route to the appropriate page method to process the callback appropriately. Listing 3 shows the server page that includes the routing logic

 

Listing 3: Partial Rendering on the Server

public partial class Ajax_SimplePageCallbacks : System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        // *** Route to the Page level callback 'handler'

        this.HandleCallbacks();

   }

 

    // Callback routing

    void HandleCallbacks()

    {

        string callback = Request.Params["Callback"];

        if (string.IsNullOrEmpty(callback))

            return;

 

        // *** We have an action try and match it to a handler

        if (callback == "HelloWorld")

            this.HelloWorldCallback();

        else if (callback == "EntryList")

            this.EntryListCallback();

        else if (callback == "StockQuote")

            this.GetStockQuote();

        else if (callback == "StockHistoryChart")

            this.GetStockHistoryChart();

 

        Response.StatusCode = 500;

        Response.Write("Invalid Callback Method");

        Response.End();

    }

 

    void EntryListCallback()

    {

        string Filter = Request.Params["Filter"] ?? "";

 

        // *** Render the data into the listview

        this.LoadEntryList(Filter);

       

        // *** Render just the list view into html

        string html = WebUtils.RenderControl(this.entriesPlaceHolder);

 

        Response.Write(html);

        Response.End();

    }

   

    void LoadEntryList(string Filter)

    {

 

        TimeEntryContext context = new TimeEntryContext();

        IQueryable<TimeEntry> q =

            from ent in context.TimeEntries

            orderby ent.TimeIn descending

            select ent;

 

        if (Filter == "Recent")

            q = q.Take(10);

        else if (Filter == "Open")

            q = q.Where(ent => !ent.PunchedOut);

        else if (Filter == "Closed")

            q = q.Where(ent => ent.PunchedOut);

 

 

        // *** Need a concrete instance so we can count

        List<TimeEntry> custList = q.ToList();

        this.lblListCount.Text = custList.Count.ToString() + " Entries";

 

        this.entriesPlaceHolder.Visible = true;

        this.lstEntries.DataSource = custList;

        this.lstEntries.DataBind();

    }

}

 

The routing is very simple – the Page_Load() early on calls HandleCallbacks() which looks at the Callback query string variable. If passed it goes into callback processing otherwise the code simply resumes normal page processing. If it is a callback the callback takes over page processing which results in full page output being sent and – eventually – a Response.End() to fire to complete page processing resulting in only the partially rendered list.

 

HandleCallbacks simply maps the Callback Id to a specific method in the Page class. If I add a new callback all I have to do is add another Id and map it to another method. Each method should return a full HTTP response – including potentially managing errors.

 

The EntryList processing loads data from Linq to SQL into the ListView control by running a query and data binding the ListView with data. Once bound a utility routine (provided with the samples) called WebUtils.RenderControl() is used to render the entire PlaceHolder containing the list view and headers and return just the HTML output. There’s also a WebUtils.RenderUserControl() which allows you to specify a user control to load dynamically and render. RenderControl is actually quite simple:

 

public static string RenderControl(Control control)

{

    StringWriter tw = new StringWriter();

 

    // *** Simple rendering - just write the control to the text writer

    // *** works well for single controls without containers

    Html32TextWriter writer = new Html32TextWriter(tw);

    control.RenderControl(writer);

    writer.Close();           

 

    return tw.ToString();

}

 

It works great for list controls like ListViews and Repeaters or simple containers like PlaceHolder. But there are a few caveats: It will only work with simple controls that don’t post back to the server even when you don’t plan on using any Postback operations. For more complex controls or containers you’ll have to use the more complete RenderUserControl method in the same WebUtils library. You can read more about these two routines in this blog post and review the code in the samples download.

 

Rendering controls is only one way to generate the HTML fragment output of course. You can hand of course code HTML output, or generate output from canned routines in your library – it doesn’t matter where the HTML comes from as long as you can return it back as  string. The same SimplePageCallbacks.aspx page contains a couple of other examples that return generated HTML data in a few of other ways.

Returning JSON from a Page Method

To demonstrate returning data, let’s reuse the stock display example, but instead of returning HTML let’s return a JSON string back. JSON stands for Javascript Object Notation and it’s an object representation format that Javascript recognizes and can evaluate natively without any manual parsing code. JSON string are simply evaluated and if valid results in a Javascript value or object.

 

If you look back at HandleCallbacks one of the routes calls the GetStockQuote() function in the page which should look familiar. Here though I use the ASP.NET AJAX JavaScriptSerializer() class to create JSON on the server and return it to the client:

 

void GetStockQuote()

{

    string symbol = Request.Params["Symbol"] ?? "";

 

    StockServer stockServer = new StockServer();

    StockQuote quote = stockServer.GetStockQuote(symbol);

 

    JavaScriptSerializer ser = new JavaScriptSerializer();

    string res = ser.Serialize(quote);

 

    Response.ContentType = "application/json";

    Response.Write(res);

    Response.End();

}

 

This code is very simple. It generates the stock quote as before by using the StockServer business object that retrieves the quote data from Yahoo and parses it into a StockQuote object. I then use the JavaScriptSerializer to turn this StockQuote object into a JSON string.

 

The generated JSON string of the serialized StockQuote looks like this:

 

{"Symbol":"VDE","Company":"VANGUARD ENRGY ET",

 "OpenPrice":0,"LastPrice":67.97,

 "NetChange":0.00,
 "LastQuoteTime":"\/Date(1227578400000)\/",

 "LastQuoteTimeString":"Nov 24, 4:00PM"}

 

 This JSON is sent to the client which requests it by calling back to the ASPX page with the Callback=GetStockQuote querystring. The code in Listing 4 demonstrates making a jQuery .getJSON() callback to retrieve and the display the stock quote.

 

Listing 4: Retrieving a JSON stock quote from the current page

function getStockQuote()

{

    var symbol = $("#" + scriptVars.txtSymbolId ).val();

   

    $.getJSON("SimplePageCallbacks.aspx?Callback=StockQuote",

              {Symbol: symbol },                   

               function(result) {    

                  $("#StockName").text(result.Company);

                  $("#LastPrice").text(result.LastPrice.formatNumber("c") ); 

                  $("#OpenPrice").text(result.OpenPrice.formatNumber("c") );

                  $("#NetChange").text(result.NetChange.formatNumber("n2") );

                  $("#QuoteTime").text(result.LastQuoteTimeString );

                  $("#divStockDisplay").slideDown("slow");                       

               });

            

    $("#imgStockQuoteGraph")

        .attr("src",

              "SimplePageCallbacks.aspx?Callback=StockHistoryChart" +

              "&symbol=" + encodeURIComponent(symbol))

        .fadeOut( function() { $(this).fadeIn(1000) });       

}

 

Notice how .getJSON() receieves the result parameter which is the deserialized StockQuote object. The anonymous function that handles the callback simply assigns the object property values to the appropriate DOM elements. Note that a few helper functions are used  from the ww.jquery.js support library you can find in your samples. Here the formatNumber function is used to format numbers to the proper numeric display.

Those pesky JSON Dates

.getJSON() works great in simple scenarios like this where you only receive a JSON result. I’m lucky however in that the date I’m interested in is provided as a string (on purpose <g>). I’m not using the LastQuoteTime property of the stock quote, but rather the preformatted string version LastQuoteTimeString which is generated on the server.

 

Take a look at the LastQuoteTime date in the JSON string above again and notice how it’s formatted. The issue is that Javascript does not a have a standard Date literal, so there’s no effective way to embed a date into JSON that is universally recognizable as a date by a parser. Microsoft uses a string that’s marked up like this:

 

"LastQuoteTime":"\/Date(1227578400000)\/"

 

This value is a special string encoding format that starts with slashes and then has a pseudo date function that contains milliseconds since 1/1/1970, which is the Javascript 0 date. The format is easy to recognize and parse which is why I suspect Microsoft created it, but it’s still a string, so if you read this value with .getJSON() you’d get back… a string rather than a date. Unless you have a parser on the client that understands this date format the date isn’t parsed and .getJSON() won’t parse it for you.

 

I’ll talk about how to address the date issue in the next section, but for now just keep in mind that JSON is a nice lean way to return data back and also send data back to the server.

JSON and Service Based Ajax

Returning JSON is a great way to build client centric applications. One of the big draws of returning and also passing back JSON to the server is that you can make very small and atomic accesses and updates to the server with very little data travelling over the wire. HTML is bulky, where JSON is much more precise and results in much smaller payloads in most cases.

 

Using JSON amounts to a more client centric approach to User Interface development. Rather than using the server to generate HTML from the current page or other pages, you can use the server as a service to return you only data. You can then use client side script code to update or create your user interface which given the flexibility that jQuery provides can often be much easier than generating the same server side HTML.

 

It’s also easier to create JSON on the server side and the approach I showed is only one of ways that you can generate JSON. Since we’re talking about a data format often times you don’t need to use ASP.NET pages (or MVC views for that matter) to generate JSON – instead you can use light weight modules or as we’ll see next WCF or ASMX web services.

 

And this is a perfect segue into the next section.

Using jQuery with WCF and ASMX Services

If you are planning on using .NET as a data service to return data to client applications, there is a rich service infrastructure in the form of Windows Communication Foundation (WCF) or ASMX Web Services available. Both platforms as of .NET 3.5 support exposing services to JSON natively. If you’re using .NET 2.0 the ASP.NET AJAX Server Extensions 1.0 can also be used to provide the same functionality.

 

When using either WCF or ASMX JSON services you have the choice of using ASP.NET AJAX to call these services using the ASP.NET ScriptManager to provide the client service calling infrastructure. I’m not going to cover this method in this article since this is covered in plenty of other places and doesn’t really affect jQuery usage. When you use ScriptManager and the client proxy generated by it you can simply call the Web Service based on the proxy generated using the class and methods exposed by it. You can then use jQuery to apply the retrieved data as shown here or in Part 1 of this article series. I have also provided the BasicWcfServicesScriptManager.aspx example that mirrors the jQuery only code I’ll describe in the next section.

Creating a WCF Service for AJAX Consumption

To create a WCF REST service that can be called with AJAX callbacks you need to do the following:

1. Add a new WCF Service to the Web Application as shown in Figure 3.


Figure 3 – Adding a new WCF service to your project

which results in a new .svc file and codebehind file to be added to your project. Make sure that your Web Application or Project is a .NET 3.5 based project since only .NET 3.5 supports WCF REST Services.


Once you’ve added the service you should see the service in the Web Application Project as shown in Figure 4. Note that if you are using stock Web Projects (rather than WAP as shown) the CodeBehind and interface file will be located in your APP_CODE folder instead.

Figure 4 – the .SVC service as shown in a Web Application.


2. Open up the .SVC file in markup mode add a the WebScriptServiceHostFactory as follows:

 

<%@ ServiceHost Language="C#"

        Service="WcfAjax.BasicWcfService"                

        CodeBehind="BasicWcfService.cs"

        Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" %>

 

This host factory is pre-configured to set ASP.NET AJAX style messaging that allows ASP.NET AJAX as well as your jQuery clients to effectively and consistently communicate with the server. The WebScriptServiceHostFactory configures requests in such a way that all requests must be made with POST and expect JSON objects with parameter properties as input, and wrapped JSON objects as output. Also, any service errors are returned as exception objects rather than raw HTML messages allowing you to effectively marshal service exceptions to the client.


3. Remove any Service configuration settings related to the new service from web.config. When the service was added by default it was added with wsHttpBinding which is a SOAP binding that won’t work with AJAX. Remove all related entries as WebScriptServiceHostFactory provides all the necessary configuration settings for you. You can still configure the service later with custom binding behavior settings if necessary, but in most cases the default behavior of the factory is sufficient.

 

If you’d like to use ASP.NET Compatibility in your Web Service to be able to access the HttpContext.Current object in your code the same way as ASMX services did you can add the following setting into the web.config file:

 

<system.serviceModel>

<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>

</system.serviceModel>


4. Set up your Service Class. By default the WCF wizard will set up your service contract interface and implementation class as two separate classes. Traditionally in WCF you define your service contract in the interface where you specify the [OperationContract] and other attributes and any custom behaviors. You then create a class that implements the interface that provides the actual operational implementation. While you’re free to implement the contract interface and class separately, for AJAX services I prefer to implement only a service class that implements both the contract and implementation on a single class as shown in Listing  6. Unlike typical services that can and often are reused with multiple protocols and different hosts and clients, AJAX services tend to be single focus application services and so to my pragmatic view at least don’t benefit from the extra layer of abstraction – I can’t foresee reusing my AJAX contract anywhere but in the local app. If you do then keep them separate. Having a single class to work with in a changeable AJAX environment is much more productive.

 

Listing 6 – A WCF Service implemented both as Contract and Implementation class

namespace WcfAjax

{

 

    [ServiceContract(Namespace = "BasicWcfService")]

    [AspNetCompatibilityRequirements(RequirementsMode =

              AspNetCompatibilityRequirementsMode.Required)]

#if DEBUG

    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]

#endif

    public class BasicWcfService

    {

        [OperationContract]

        public StockQuote GetStockQuote(string symbol)

        {

            StockServer server = new StockServer();

            StockQuote quote;

 

            return server.GetStockQuote(symbol);

        }

 

        [OperationContract]

        public StockQuote[] GetStockQuotes(string[] symbols)

        {

            StockServer server = new StockServer();     

            return server.GetStockQuotes(symbols);

        }

   }

}

Using only jQuery to call WCF

To call this service only using jQuery we’ll need to do a little bit of work. While jQuery has support for basic JSON functionality in the form of  the .getJSON() function, this method isn’t adequate for calling the WCF or ASMX services properly.  There are two problems with .getJSON(): It doesn’t support client side JSON serialization natively and it doesn’t know how to deal with ASP.NET style date formats. To address this we’ll need:

 

  • MS AJAX aware JavaScript JSON Serializer
    jQuery does not include a JSON serializer. Since WCF/ASMX require parameters to be sent as POST JSON objects, a serializer is required on the client. The standard getJSON() deserialization also doesn’t work with the MS date format ("LastQuoteTime":"\/Date(1227578400000)\/")  so special handling is required for deserialization as well. For this task I’ve provided a modified version of Douglas Crockford’s JSON2.js or ww.jQuery.js both of which include JSON parsers that understand the Microsoft date format both for serialization and deserialization.

  • A custom Service Callback Handler
    Making a WCF callback requires setting quite a number of options on the $.ajax() function and the results coming back have to be handled properly in order to yield consistent values. WCF results need to be ‘unwrapped’ and any $.ajax() errors need to be normalized so that an error object is returned. To facilitate this process I’ve provided the small ServiceProxy class in Listing 5 as well as in ww.jquery.js (which provides a slightly more complete version).

 

Let’s take a look and see what this looks like when calling the above Web Service using the helpers I mention above. In the following example I allow the user to enter a set of stock symbols and retrieve a set of StockQuotes objects that are then rendered into a list view like display on the client as shown in Figure 5. This list is client rendered.

 

Figure 5 – Example of a WCF Service providing data to a jQuery client with client rendering.

 

Unlike the Page examples earlier, in this example the server only provides JSON data rather than HTML to the client. The client renders the result by using an empty ‘fake template’ that exists in the HTML document and filling in the ‘data holes’ with the data retrieved from the server for each of the retrieved stock quotes.

 

Listing 7 shows the Javascript code used to make the callback to the server and handle the updating of the display when the callback returns using the ServiceProxy class.

 

Listing 7 – Retrieving a set of stock quotes and displaying them using a ‘template’

// *** Create a global instance

var serviceUrl = "BasicWcfService.svc/";

var proxy = new ServiceProxy(serviceUrl);

function getStockQuotes() {

    var symbols = $("#" + serverVars.txtSymbolsId ).val().split(",");

 

    proxy.invoke("GetStockQuotes",

         { symbols: symbols },  // pass symbol array as 'symbols' parameter

         function(quotes) {  // result is an array for each of the symbols              

             var jCnt = $("#divStockDisplay").fadeIn("slow");

             var jDiv = $("#divStockContent").empty();

 

             // quotes is an array

             $.each(quotes, function(index) {

                 var jCtl = $("#StockItemTemplate").clone();

                 jCtl.attr("id", "stock_" + this.Symbol);
                 var symbol = this.Symbol;

 

                 jCtl.find(".itemstockname").text(this.Company);

                 jCtl.find("#tdLastPrice").text(this.LastPrice.formatNumber("n2"));

                 jCtl.find("#tdOpenPrice").text(this.OpenPrice.formatNumber("n2"));

                 jCtl.find("#tdNetChange").text(this.NetChange.formatNumber("n2"));

                 jCtl.find("#tdTradeDate").text(this.LastQuoteTimeString);

                 jCtl.fadeIn().click(function() { alert('clicked on: ' + symbol); });

                 jCtl.find(".hoverbutton").click(function(e) {

                                    alert("delete clicked on: " +

                                    symbol); e.stopPropagation(); });

 

                 jDiv.append(jCtl);

             });

         },

         onPageError);                         

}

function onPageError(error){

    alert("An error occurred:\r\n" + error.Message);

}


The sample page uses the ServiceProxy class for the callback a global instance of this class is created. ServiceProxy receives the URL of the service to call including the trailing backslash when instantiated. Once instantiated the .invoke() method of the proxy instance can be called to make a callback to the server.

 

When calling the GetStockQuotes method the code first retrieves the input field value containing the comma delimited symbols and splits them up into an array as the server method expects. I use a shortcut here for Client Ids by way of a custom control called ScriptVariables (provided in jQueryControls.dll) that exposes all ClientIds as properties of the serverVars object. So serverVar.txtSymbolsId contains the ClientID of the txtSymbols control. The user enters symbols as a comma delimited list which is .split() and turned into an array of symbol strings that the service method excepts as an input parameter.

 

Next the service is called with proxy.invoke(). You pass this method the name of the service method to call plus any parameters which are provided in the form of an object with each parameter a property of the object. So the service method is defined like this:

 

[OperationContract]

public StockQuote[] GetStockQuotes(string[] symbols)

{

    StockServer server = new StockServer();     

    return server.GetStockQuotes(symbols);

}

 

On the client I’m passing an object that has a symbols property with the symbols array as a value:

 

{ symbols: symbols }

 

If you had multiple parameters you’d express them as an object with multiple properties with each property matching the server parameter names:

 

{ symbol: "MSFT", years: 2 }

 

In the GetStockQuotes call the invoke method serializes the symbols array into JSON and sends it to the server for processing. The server side method in the WCF service class receives the symbol array and then retrieves a set of quotes as an array that is returned to the client. The JSON result is an array of StockQuote objects which WCF returns like this:

 

{"d":

   [{"__type":"StockQuote:#WcfAjax",

     "Company":"LDK SOLAR CO ADR",

     "LastPrice":15.48,

     "LastQuoteTime":"\/Date(1227913260000-1000)\/",

     "LastQuoteTimeString":"Nov 28, 1:01PM",

     "NetChange":1.35,

     "OpenPrice":14.40,

     "Symbol":"LDK"

    },

    {"__type":"StockQuote:#WcfAjax",

     "Company":"MKT BCTR GBL ALT",

     "LastPrice":21.00,

     "LastQuoteTime":"\/Date(1227913140000-1000)\/",

     "LastQuoteTimeString":"Nov 28, 12:59PM",

     "NetChange":0.44,

     "OpenPrice":20.27,

     "Symbol":"GEX"

    }    

   ]

}

 

Note the “root level wrapped property” that’s typical of WCF and ASMX services and accounts for the “wrapped” format I’ve been talking about. The root property then contains the actual result, which is an array of two stock quote objects. When the actual result is returned to the client the callback should receive only the actual result value which is the array of quotes. The ServiceProxy class takes care of this task, so when the callback is made successfully,  only the array of quotes is passed to the callback function not the whole evaluated WCF structure.

 

If you look back on Listing 7 you see that the third parameter is a callback handler and it receives the result as an array of quote objects. The code that follows then parses through the array and effectively adds new items into the following placeholder in the HTML document:

 

<div id="divStockDisplay" class="blackborder" 
        style="display:none;width: 600px;">

    <div class="gridheader">Stock Results</div>           

    <div id="divStockContent" style="overflow-y: scroll;height: 200px;">           

    </div>

</div>

 

The code starts out by making the stock display visible and fading it in as it’s initially not displayed. Then all the content in divStockContent is cleared out since we will be loading a new set of items into the display container. Next the code loops through each of the quote objects using jQuery’s static $.each() function. $.each() loops over each item and calls the specified function in the context of the item parsed – in this case a StockQuote which is exposed as the this pointer.

 

Inside of the .each loop then code then proceeds to load up the ‘template’. I call it a template, but really it’s just a fragment of hidden HTML in the page that holds the empty layout of a stock quote without data:

 

<div id="StockItemTemplate" class="itemtemplate" style="display:none">

    <div class="stockicon"></div>

    <div class="itemtools">

        <a href='javascript:{}' class="hoverbutton" ><img src="../images/remove.gif" /></a>

    </div>

    <div class="itemstockname"></div>

    <div class="itemdetail">

        <table cellpadding="3"><tr>

            <td>Price:</td>

            <td id="tdLastPrice" class="stockvaluecolumn"></td>

            <td>Open:</td>

            <td id="tdOpenPrice" class="stockvaluecolumn"></td>

            <td>Change:</td>

            <td id="tdNetChange" class="stockvaluecolumn"></td>               

            <td id="tdTradeDate" colspan="2"></td>

        </tr></table>

    </div>

</div>


The code picks up the empty template from the page and simply clones it, which creates a new DOM element:

var jCtl = $("#StockItemTemplate").clone().show();

 

The result is a new jQuery object of that new as of yet unattached DOM element. One important thing to set is the ID property of the item so that each element can be uniquely identified later, so

var ctl = jCtl.attr("id","stock_" + this.Symbol;

 

accomplishes that task. The code then stuffs the stock quote data into the appropriate ‘holes’ in the template by using the find command and setting the .text() of the element like this:

 

jCtl.find(".itemstockname").text(this.Company);

 

Finally when the item has been all updated it gets added to the bottom of the list with:

 

jDiv.append(jCtl);

 

which adds the newly cloned and formatted element to the content container. Voila, we’ve just called a WCF service with an array input and output value and nice jQuery logic to dynamicly render the results on the client.

The ServiceProxy simplifies Service Calls

The code above is not much more involved than the code you’d write with a ScriptManager generated proxy. The main difference is that there is no proxy (and no Intellisense) and you end up calling the .invoke() method with a method string and object for parameters rather than a simple method on the proxy. The rest of the behavior is pretty much the same.

 

The ServiceProxy class is basically a wrapper around the jQuery $.ajax() function that uses a slightly modified version of JSON2.js to handle serialization and deserialization and ‘unwrapping’ of success and error responses so the behavior is consistent with object/value results returned in all situations. Listing 5 shows the relatively short implementation of the ServiceProxy class.

 

Listing 5: A WCF/ASMX client ServiceProxy for making AJAX calls with jQuery

this.ServiceProxy = function(serviceUrl) {

    /// <summary>

    /// Generic Service Proxy class that can be used to

    /// call JSON Services using jQuery.

    /// Depends on JSON2.js modified for MS Ajax usage

    /// </summary>

    /// <param name="serviceUrl" type="string">

    /// The Url of the service ready to accept the method name

    /// should contain trailing slash (or other URL separator ?,&)

    /// </param>

    /// <example>

    /// var proxy = new ServiceProxy("JsonStockService.svc/");

    /// proxy.invoke("GetStockQuote",{symbol:"msft"},

    ///              function(quote) { alert(result.LastPrice); },onPageError);

    ///</example>

 

    var _I = this;

 

    this.serviceUrl = serviceUrl;

 

    this.invoke = function(method, params, callback, error) {

        /// <summary>

        /// Calls a WCF/ASMX service and returns the result.

        /// </summary>   

        /// <param name="method" type="string">The method of the service to call</param>

        /// <param name="params" type="object">An object that represents the parameters to pass {symbol:"msft",years:2}      

        /// <param name="callback" type="function">Function called on success.

        /// Receives a single parameter of the parsed result value</parm>

        /// <param name="errorCallback" type="function">Function called on failure.

        /// Receives a single error object with Message property</parm>

 

        // Convert input data into JSON - REQUIRES modified JSON2.js

        var json = JSON2.stringify(params);

 

        // Service endpoint URL       

        var url = _I.serviceUrl + method;

 

        $.ajax({

            url: url,

            data: json,

            type: "POST",

            processData: false,

            contentType: "application/json",

            timeout: 10000,

            dataType: "text",  // not "json" we'll parse

            success: function(res) {

                if (!callback) return;

 

                // Use json library so we can fix up MS AJAX dates

                var result = JSON2.parse(res);

 

                // Wrapped message contains top level object node

                // strip it off

                for (var property in result) {

                    callback(result[property]);

                    break;

                }

            },

            error: function(xhr) {

                if (!error) return;

                var res = xhr.responseText;

                if (res && res.charAt(0) == '{')

                    var err = JSON2.parse(res);

                if (err)

                    error(err);

                else

                    if (xhr.status != 200)

                    error({ Message: "Http Error: " + xhr.statusText });

                else

                    error({ Message: "Unknown Error Response" });

                return;

            }

        });

    }

}

 

Most of the work is handled by the jQuery $.ajax() function that performs the actual AJAX callback. The .invoke method first manually serializes the input parameters using JSON2 before making the $.ajax() call. $.ajax() calls either a success or error function when implemented. On success the HTTP string result is unpacked, deserialized and the first ‘property’ value is used as the result value that is passed to the user provided callback function.

 

The biggest chunk of code deals with errors. $.ajax() errors can come in several different forms from protocol level errors to errors in the service code and the error code basically checks for a JSON object which WCF returns on service level errors. If a protocol error occurs the response is HTML and so the status code is retrieved and returned.

Handling Server Side Exceptions

The way error handling works means that you can throw exceptions on the server and receive those exceptions on the client as part of the error callback. Consider the following service method:

 

[OperationContract]

public string ThrowServerException()

{

    throw new InvalidOperationException(@"User generated Error.
               This error has been purposefully created on the server"
);

    return "Gotcha!";

}

 

which can be called and produce expected results with this code on the client:

 

function throwServerException() {

 

proxy.invoke("ThrowServerException",

             null,

             function(result) {

                 alert("This should never be called.");

             },

             function(error){

                  alert("An error occurred:\r\n\r\n" + error.Message);

             });

}

 

In this case the error function is called error.Message is going to be “User generated Error. This error…”.

 

Using WCF in combination with the ServiceProxy makes it very easy to create new callbacks on the server: Create a method in the service and then simply call with the ServiceProxy class’s .invoke() method.

 

If you want to take a look at a more involved sample application you can check out the JsonStockClient.aspx example, which uses the ServiceProxy class create a rich user interface entirely on the client side against a WCF Service interface.

Figure 6 – The StockPortfolio Sample application demonstrates a host of WCF features with the ServiceProxy jQuery client.

 

This application lets you retrieve stock quotes and manage a simple stock portfolio that lists current pricing of stocks that you have added into the portfolio. A number of the concepts I’ve discussed are used in this project as well as the WCF functionality that allows returning image streams from the WCF service which is used for graphs.

Creating Client Content with Templates

One other feature of interest in this example is templating. I’ve mentioned templating repeatedly in the first part of the article and again earlier when I used the ‘data holes’ templating approach of cloning content and then embedding it into the page. While this approach works it’s still fairly work intensive as you have to explicitly use jQuery expressions to find the ‘holes’ in the template and fill in the blanks.

 

There are a number of jQuery templating solutions available. I’ve used jTemplates (which has a Python like templating language) successfully for some time, but I’ve recently switched over to a customized version of John Resig’s Micro Templating Engine which is very compact but uses plain JavaScript to handle the templating. The beauty of John’s engine is that it’s tiny and entirely uses a language that you should already be familiar with – Javascript.

 

Because it’s so compact I’ve integrated the Micro Templating Engine with a few small modifications into my client library (ww.jquery.js). My customizations from John’s code change the template delimiters from <% %> to <# #> to avoid issues with ASP.NET’s page parser, add some basic error handling and display and fix a small bug that has to do with quote characters. Listing 6 shows my version of the parseTemplate() function that is based on John’s original base code.
 

Listing 6 – A powerful but lightweight Javascript Template Engine

/// based on John Resig's Micro Templating engine

var _tmplCache = {}

this.parseTemplate = function(str, data) {

    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('');";

 

            func = new Function("obj", strFunc);

            _tmplCache[str] = func;

        }

        return func(data);

    } catch (e) { err = e.message; }

    return "< # ERROR: " + err.htmlEncode() + " # >";

}

 

The idea of a template engine is that you can create markup as you normally do, but embed the data as Javascript expressions right into the content using tag delimiters. Hey we know how to do that already in ASP.NET with <% %> tags. The difference is we want to do this on the client rather than on the server.

 

For example, in the StockPortfolio application the individual user portfolio items are rendered using the parseTemplate() function. When the page first loads no data is rendered into the list. Instead client script requests the portfolio items as an array which is then parsed one item at a time using the template. The same template is also used to update items when the user changes a quantity or symbol or adds new items.

 

Templates allow you to design the layout and data display in one place and re-use that template, potentially in multiple places.

 

The template for an individual portfolio item looks like this:

 

<script type="text/html" id="StockItemTemplate">

<div class="itemtemplate" style="display:none">  

    <div class="stockicon"></div>   

    <div class="itemtools">

        <a href="javascript:{}" class="hoverbutton">
          <img src="../images/remove.gif" /></a>

    </div>

    <div class="itemstockname"><#= Symbol #> - <#= Company #></div>

    <div class="itemdetail">

        <table style="padding: 5px;"><tr>

            <td>Last Trade:</td>

            <td id="tdLastPrice" class="stockvaluecolumn"><#= LastPrice.formatNumber("n2") #></td>

            <td>Qty:</td>

            <td id="tdLastQty" class="stockvaluecolumn"><#= Qty #></td>

            <td>Holdings:</td>

            <td id="tdItemValue" class="stockvaluecolumn"><#= ItemValue.formatNumber("c") #></td>               

            <td id="tdTradeDate" colspan="2"><#= LastDate.formatDate("MMM dd, hh:mmt")#></td>

        </tr></table>

    </div>

</div>

</script>   

 

Note the <script> tag which is a great way to hide the template from normal HTML markup. The above is valid HTML and any XHTML parser and robot will simply ignore this script block as if it wasn’t there. Yet the content of the script – the template string - can still be retrieved based on its id. This template is passed a stock quote object and the properties of the quote are accessed inside the <#=  #> expressions. The expressions are plain Javascript expressions and you can see that some of the values call functions like formatNumber and formatDate which are part of the ww.jquery.js library. You can also embed code blocks into the page to loop through items which I’ll show a little later.

 

Let’s look and see how the client code can retrieve the stock quote data. Listing 7 shows the code to retrieve the portfolio items and then iterate through them and merge them into the template one at a time.

 

Listing 7 – Updating a List of Items using Client Templating

function LoadQuotes(noMsg)

{   

    proxy.invoke("GetPortfolioItems",

         { userToken: userToken },

         function(message) {

             $("#lstPortfolioContainer").empty();

                 

             $.each(message.Items, function(i) {

                 var item = this;   // this is the iterated item!

                 var newEl = UpdatePortfolioItem(item);

             });

         },

         OnPageError);

}

function UpdatePortfolioItem(stock) {

   

    // Retrieve the Item template

    var template = $("#StockItemTemplate").html();

   

    // Parse the template and merge stock data into it

    var html = parseTemplate(template, stock);

 

    // Create jQuery object from gen'd html

    var newItem = $(html);

 

    // See if we have an existing item

    var origItem = $("#" + stock.Pk + "_STOCK");   

 

    if (origItem.length < 1)

        newItem.appendTo("#lstPortfolioContainer");

    else

        origItem.after(newItem).remove()                  

 

    newItem.attr("id", stock.Pk + "_STOCK")

      .click(function() { ShowStockEditWindow(this) })     

      .fadeIn(1500);

 

    return newItem;

}

 

There’s not a lot of code in this sample and that’s really the point. The only thing of interest in regards to templating is the retrieval of the template and then merging it with these two lines of code:

 

    var template = $("#StockItemTemplate").html();

    var html = parseTemplate(template, stock);

 

The first line simply returns the template as a string from the script block. The second then merges the stock quote into the template and returns a string result of the merged data for an individual stock item. The html is then turned into a jQuery object that then either replaces an existing item or adds a new one to the list. The UpdatePortfolioItem() function is called from LoadQuotes() as shown, but it’s also called from UpdatePortfolioItem() which updates or adds new items to the list. So this function and the template are re-used in multiple locations without duplicate code or markup.

 

One piece of code, one piece of markup all maintained in one place – that’s the benefit of using templates.

 

parseTemplate() can also work with code blocks so you can do things like loop through a list. The following is another example provided in BookAdmin.aspx. One of the forms displays a search result from Amazon as list that looks as shown in Figure 7.

 

Figure 7 – The Amazon Book list uses client side Javascript templating to render the list

 

In the previous example I used a single item template to add one item at a time to the list and used Javascript code to loop through the items. In this example, a single html string is generated for the entire list based on the template and the template does the iteration. The following template uses code blocks and a for loop to run through all of the books.

 

<script type="text/html" id="amazon_item_template">   

<# for (var i=0; i<bookList.length; i++) {

    var book = bookList[i];

#>

<div class="amazonitem" ondblclick="selectBook(this);" tag="<#= book.Id #>">

    <img src="<#= book.SmallImageUrl #>" class="imgAmazonSearch"/>

    <div><b><#= book.Title #></b></div>

    <div><i><#= book.Publisher #> &nbsp;
           (<#= book.PublicationDate #>)</i></div>

    <small><#= book.Author #></small>

</div>

<# } #>

</script>

 

Note the <# #> blocks that allow any sort of Javascript code to be embedded. parseTemplate effectively works by taking the template and turning it into an executing Javascript function and so just about all Javascript code can be used in code blocks. You can loop, you can use if statements to display content conditionally and you can access any global code that is in scope.

 

Templating is an extremely powerful mechanism for building rich client applications without having to generate HTML by hand, filling holes explicitly and most importantly not repeating yourself when creating HTML markup.

 

parseTemplate() is only one templating mechanism but the general concepts are similar in most template engines. There are a host of other template solutions available for jQuery. I’ve also used jTemplates for quite some time prior to this implementation and it works well, and there are several other engines available that I haven’t used.

Using AjaxMethodCallback in jQueryControls

The BookAdmin example actually uses yet another callback mechanism which is provided as part of the jQueryControls project. This project contains an AjaxMethodCallback control which can make JSON callbacks to the same page, a user or server control on the same page or an external HttpHandler to handle JSON callbacks. The control is based on a combination of client script and a server wrapper control that combine to provide the callback capability. The server control is optional – you can also use the AjaxMethodCallback client library on its own and it operates similar to the way ServiceProxy works.

 

The easiest way to use this functionality is to use the control and drop it onto the page or insert it via markup:

<ww:AjaxMethodCallback ID="Proxy" runat="server" />

 

This uses all default settings which allow to call back to the current page and post back only method parameters. Alternately you can call an external handler and specify how POST data is to be sent.

 

<ww:AjaxMethodCallback ID="Proxy" runat="server"

        ServerUrl="AdminCallbackHandler.ashx"

        PostBackMode="PostNoViewstate" />

 

In the BookAdmin page callbacks are made using an ASHX handler which is the most efficient way, but realize that the methods of the handler shown below could easily have been created as methods of the current form.

 

Like WCF and ASMX services you can create simple methods that are called from the client and receive JSON parameters and return a JSON response. Here’s is a single method in the BookAdmin.ashx handler implementation:

 

/// <summary>

/// Callback handler for Amazon Books Page

/// </summary>

public class AdminCallbackHandler : CallbackHandler

{

    private busBook books = new  busBook();

 

 

    [CallbackMethod]

    public List<AmazonBook> GetBooks(string filter)

    {

        if (filter == null)

            filter = string.Empty;

 

        IQueryable<AmazonBook> bookList = null;

 

        if (filter == "Highlighted")

            bookList = books.GetHighlightedBooks();

        else if (filter == "Recent")

            bookList = books.GetRecentBooks();

        else

            bookList = books.GetBooks();

 

        return bookList.ToList();

    }
}

 

The handler needs to inherit from CallbackHandler which provides all the necessary plumbing to marshal the request to the appropriate method, call it and return a result back to the client.

 

On the client the server code is easy to call and uses a client proxy that works in the same way that the WCF/ASMX proxy works. The proxy is created with the same name as the AjaxMethodCallback control’s Id – Proxy in this case and has methods for each of the exposed server methods. Each method has the same parameter signature as the server method plus the callback and errorCallbacks – just like service proxies discussed earlier. To call the server is as easy as:

 

function editBook(ctl)

{
    var bookPk = $(ctl).attr("id").replace("book_","");
    bookPk = parseInt(bookPk);

    Proxy.GetBook(bookPk,

                  function(book) {  // object result

                      alert(book.Title + " " + book.AmazonUrl);

                      ShowBookForm(book);

                  },

                  onPageError);

}

 

The actual page code in the sample of course is a bit more complex and it uses jQuery to pop up and display the edit window and populate its form fields. What is nice about AjaxMethodCallback is that it’s fully self contained and loads up jQuery.js and the ww.jquery.js script library (optionally – you can override resource load behavior) so you drop the control add methods and you’re on your way, and it doesn’t require any of the ASP.NET AJAX or WCF components.

 

One additional benefit of this control is also the ability to use it to handle callbacks in page and control code. This means if you build a custom ASP.NET server control that needs to retrieve data from the client directly (rather than through a service reference), it’s easy to do by simply adding an AjaxMethodCallback to the page’s controls collection and pointing it at the class that will handle callbacks. Any class can be pointed at to handle callback events.

Summary

Phew – a lot of options for you to calling back to the server using jQuery. In this article I’ve covered both manually using ASP.NET pages to handle callback routing and returning both HTML and JSON data to the client. I’ve also covered three – count ‘em – mechanisms for making JSON based server callbacks that let you pass data between client and server.

 

Which approach is best? As always it depends.

 

For existing applications that need to add a little bit of AJAX functionality page level callbacks that return HTML fragments can be a quick way to provide little bits of content to update the client. Control or user control rendering can provide reuse of server side components, or you can generate HTML from scratch to return to the client. Personally I prefer the ‘raw’ Ajax approach which uses the server as a data service for serving JSON data to the client and then update the client using either small chunks of DOM manipulation or for more complex or markup intensive items using templates. It’s a little more work but you can do so much more interesting stuff and you have way more control over your user interface.

 

When it comes to callbacks to the server you have lots of choices. I discussed three callback mechanisms – WCF, ASMX and AjaxMethodCallback. WCF or ASMX are no brainers if you’re already using ASP.NET AJAX. If you’re not using ASP.NET AJAX you can still use either WCF/ASMX on the server and use the ServiceProxy class and the JSON parsers I introduced in the article on the client. Doing so lets you avoid loading the ASP.NET AJAX client library which is overkill if you only need to make service callbacks.  AjaxMethodCallback is very easy and fully self contained so you can just drop a control and you’re ready to go. It also is a bit more flexible since you can use the server component on a Page, a user or server control, in a high performance Http Handler or even your own custom handling code via the generic JsonCallbackMethodProcessor class which does all the dirty work of parsing input and output and generating JSON.

 

All approaches make it easy to make callbacks to the server with literally just a single line of code, so that you can focus on using the data returned effectively and use jQuery to update the UI. After all that’s really what it’s all about – service callbacks should be just plumbing and the logic of applying the results is where the real work usually comes in and that’s where jQuery really excels. If you’re building complex or repetitive UI layouts client side make sure you check out templating for merging Javascript based templates with data. Templates make sure you don’t repeat yourself and keep your layout in one place even though you are working on the client side.

 

I hope Part 2 of this article has been useful to you. In Part 3 I’ll look a bit closer at integration between ASP.NET and jQuery in terms of creating server controls. I touched on this topic a little with the AjaxMethodCallback control which integrates client jQuery components and plug-ins and a server control. I’ll look deeper into how you can build reusable ASP.NET components that interact with jQuery. Until have fun calling server side code with jQuery.



 

kick it on DotNetKicks.com

 

Resources

Sample Download:

Components described in this article are also part of the
West Wind Web Toolkit for ASP.NET

 

Talk Back


jQueryControls


Web Sites

 

Printable Cheat Sheets

 

Books

 

 

West Wind Technologies © Rick Strahl, West Wind Technologies, 2008