Yan-Feng

记录经历、收藏经典、分享经验

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Overview

You're not cool if your web site doesn't have AJAX implemented in one place or another. At least that's the impression you get in the post-Web 2.0 development world. For this chapter, the authors are going to assume that you know what AJAX is and understand the technologies that make AJAX possible — in other words we're not going to dive into the history and theory of it all.

Rather, we're going to jump right into code and show you how to implement AJAX functionality in your ASP.NET MVC application using the most popular (and free!) JavaScript libraries:

  • Microsoft ASP.NET AJAX

  • jQuery

This chapter is devoted to showing you how to implement the most common AJAX tasks in an ASP.NET MVC web application with each of the preceding libraries. As with any technology, however, there is always a right time and place for using AJAX — and it's worth discussing this right up front.

 

When AJAX is Cool

Asynchronous requests have their advantages and can make users feel like they are in control of your application rather than the other way round. Some of the advantages include:

  • Partial rendering of a complex page: If a particular page you're working on (a portal page, for instance) has a lot of dynamic information on it, it might be advantageous to avoid doing a full postback to the server simply to update a small section of the page. A user may want to filter a list of information, for example, by a selection in a dropdown. Refreshing the entire page (database calls and all) in this case can create a performance bottleneck and give the impression that the site is slow or cumbersome to work with.

  • It looks fast: AJAX requests, when properly setup and executed, can be quite fast. When you couple this with the lack of a page "blink" (from a postback) users tend to come away with the impression that your site is much more responsive even when the response time is roughly the same. This lends to a greater sense of control and interactivity.

  • Reduced server load: As discussed in the first bullet, careful use of AJAX can reduce the overall load on your server and database. This isn't confined to complex pages but applies to the entire site operation. If the main functional elements of a page can be confined to smaller AJAX operations, it can significantly cut down on database and/or server connections.

  • It's asynchronous: The fact that AJAX is asynchronous (that's the "A" in AJAX) allows you some flexibility in terms of long-running operations or executing things that a user may otherwise not want to wait around for. Google's Gmail application (one of the first major well-known AJAX application) is a prime example of clever use of asynchronous requests. Most of the time you're not aware of the page operations as you read your messages or navigate the site, only while you're occupied with what's on-screen, Gmail is constantly checking the server for updates to conversations (mail messages) and IM status (if you have that enabled).

 

When it's Not

It's very easy to become enamored with the functionality that AJAX offers. Indeed AJAX has been the crutch of many project managers over the years ("just AJAX it!") and the darling of web developers who want to add a feeling of "slickness" to their web sites.

This, as you can imagine, can lead to overuse of the technology and create some problems. Some of these problems are:

  • JavaScript must be enabled on the client: For many developers, this is a very foreign concept: a user might actually turn off scripting in their browser. There are a lot of reasons that people do this, not the least of which is security. Clever programming of JavaScript leads to cross-site scripting attacks, stealing cookies, and even planting various password-stealing Trojans. Many people disable JavaScript as a matter of habit, and these people will be unable to use your site if it relies on AJAX.

    Important 

    Product Team Aside

    Rob likes to play World of Warcraft (WoW) and considers himself a fairly casual player, logging in a couple of times a week after work. One evening, when his kids were in bed and the house was quiet, Rob logged in to play his favorite mage and found that his password had been changed. He went to the WoW web site and reset his password and when he logged back in again, he found that everything on his character was gone — his account had been hacked.

    In the coming weeks, it was found that a very popular WoW web site had a script-based Trojan in one of its banner ads. This Trojan was what they call a "keylogger" — it sat in memory in your browser and tracked your keystrokes and sent them back to a central site, which tried to deduce which word pair was your username/password. This Trojan had resulted in the stealing of thousands upon thousands of accounts, and made people very unhappy. The easiest way to avoid having your keys logged and your account stolen? Disabling scripting in your browser. Many developers don't understand why user would do this kind of thing. The answer: They like to keep clothes on their mage.

  • The Back Button and bookmarks: Usability is a primary feature of every web site, and your browser's history and Back button are primary considerations. Users should be able to "undo" their navigation and step backward from whence they came. AJAX doesn't (normally) allow for this. If you re-render an entire page using AJAX, or change the display significantly, you're presenting information that may not reflect the URL properly. Thus, a user will lose his or her "crumb trail" through your site, and navigation becomes somewhat useless (though ASP.NET 3.5 AJAX SP1 does address some of these concerns with its browser history functionality).

    This includes bookmarks. Users need to have the ability to return rapidly to a bit of information on your site that they are interested in. If they need to click various buttons or select options in a dropdown to get the page to render a certain way, the bookmark becomes meaningless.

  • Just plain abuse: When AJAX first came out, many prominent web sites implemented it wherever it would fit with what can only be termed as "blind abandon." The issue that many of these zealous developers didn't consider was the client's browser and its ability to properly and effectively load a given page.

    For many users of the Web, JavaScript just isn't fast. This lead to longer than usual page load times and defeated almost entirely the major appeal of AJAX in the first place. In fact, it wasn't uncommon to see script files reaching sizes of 500K and up — and these would need to be downloaded from the server (which takes a while on slower connections) and loaded into the browser's memory, and often these scripts would issue requests for even more information from the server!

Important 

Product Team Aside

Microsoft's first major foray into an AJAX web property was Start.com. The idea at the time was to create a user-defined portaling system, much like an MSN user's home page, and "AJAXify it." The idea was a nice one, but it wasn't uncommon to wait for upwards of 20 seconds for the page to load and for some of the pages sections to timeout entirely.

The problem only intensified as you added more sections to the page — with each section making a single request back to the server for information. When the page loaded, it absolutely pegged bandwidth for some users and ultimately became completely unusable.

There were other issues with it as well, such as "what's the point?" since it compared very evenly with Yahoo!'s My Yahoo! and (as mentioned previously) the already established MSN Home portal. Start.com was soon meshed with Microsoft's Live.com initiative, and the portal was scrapped entirely.

A lot of this is subjective, and the authors fully understand that it may also be completely out of your control as a developer — especially when your boss goes to conferences or reads trade magazines that tout a technology as the Next Big Thing. Hopefully, however, you will be able to implement the following samples in a safe and sane way.

 

AJAX Examples

As with most things, there are many ways to implement AJAX functionality with a web site. Most of the technology that's required is encapsulated in the scripting library you choose, but you do have to pay attention to how your server code is constructed to make sure that it follows MVC and is easily changeable.

For the examples we are about to show, we're going to follow a fairly consistent pattern for constructing the server-side code:

  • Each AJAX routine will use a dedicated Action on a Controller.

  • The Action will check to see if the request coming in is an AJAX request.

  • Each Action will return a dedicated view, unless the action is not an AJAX request.

The last bullet here is critical in understanding the proper implementation of AJAX and resolves down to something mentioned earlier: What if the client doesn't have scripting enabled? If this is the case, your site will not render the page properly and will confuse the user completely!

Handling Disabled Scripting

Let's take the case where you want to do the simplest operation: update a DIV element on your page with search results when a user clicks the "search" button. You want this form to post asynchronously to the server, and you're going to take the results, as rendered, and populate a DIV element on the page (let's call it "results"):

<% using(Html.BeginForm(new {action = "HelloAjax"})) { %>    <input type=" text" name=" query" size="40" />    <input type=" submit" value=" go" />    <% } %>    <div id=" results">    </div>

You can easily set this up to be an AJAX form (using the Microsoft AJAX libraries, which come with ASP.NET MVC) by using the AJAX Helpers that come with ASP.NET MVC:

<%using (Ajax.BeginForm("HelloAjax",          new AjaxOptions { UpdateTargetId = "results" }))  { %>    <%= Html.TextBox("query", null, new {size=40}) %>    <input type=" submit" />  <%} %>   <div id=" results">   </div>

The next thing to do (addressed more later in the chapter) is to "wire up" the page, referencing the required client scripts, which allow the form to work:

<script src="/Scripts/MicrosoftAjax.js" type=" text/javascript"></script><script src="/Scripts/MicrosoftMvcAjax.js" type=" text/javascript"></script>

Finally, let's create a very simple Action (knowing it's incorrect) that will render the search string back to us as the form result:

public string HelloAjax(string query){    return "You entered: "+ query;}

Note that you're taking advantage of implicit action results as covered in Chapter 5. Indeed there's a method to our madness here — the first is to show you that this will, indeed, work as you can see in Figure 7-1.

Image from book
Figure 7-1

Quite nice! It didn't take much effort, and all of the scripting details are handled by the framework, which is a nice time saver.

If you disable JavaScript, however, you run into some problems. To understand this, take a look at the source that's rendered for the search form by the framework:

<form action="/Home/HelloAjax" method=" post"onsubmit=" Sys.Mvc.AsyncForm.handleSubmit(this, new Sys.UI.DomEvent(event),{ insertionMode: Sys.Mvc.InsertionMode.replace, updateTargetId: ‘results' });"><input id=" query" name=" query" size="40" type=" text" value=" "/>    <input type=" submit" /></form><div id=" results"></div>

This code wires up the onsubmit form event to an internal function contained in MicrosoftMvcAjax.js, which is responsible for handling the form post as well as updating the innerHTML of the "results" div (it can do a lot more than this — you'll see this in action later in the chapter).

If you turn off scripting in the browser, the onsubmit event is never called and, therefore, the form will default to its natural behavior, which is posting the itself back to the server. The good news is that there are no errors when this happens; the bad news is that it's a less than optimal experience for the end users, as all they will see is the rendered output of the Action.

All visual elements are lost now, and even the Master Page isn't rendered because the action isn't calling a view — it's just issuing a Response.Write call. As you can imagine, this doesn't look very nice, as you can see in Figure 7-2.

Image from book
Figure 7-2

This is one area where having a more complete framework (such as ASP.NET AJAX running with ASP.NET Web Forms) can save you from some of the issues that you may not immediately recognize. On the other hand, frameworks can impose themselves heavily on your design and remove a measure of control that you, as a seasoned developer, may really need.

The ASP.NET MVC team thought about this problem (and many others) when developing the AJAX component of ASP.NET MVC. When you make an asynchronous request to the server, you want to let the server know what you're up to and, happily, the ASP.NET MVC AJAX implementation does this.

To illustrate this, reenable JavaScript on your browser and submit the form again, taking a look at the request, using Fiddler (a free tool for analyzing web traffic from your browser available from www.fiddlertool.com). The following snippet shows the contents of the request:

POST /home/HelloAjax HTTP/1.1Accept: */*Accept-Language: en-usReferer: http://localhost.:55926/homex-requested-with: XMLHttpRequestContent-Type: application/x-www-form-urlencoded; charset=utf-8...query=Hello%20Ajax!&X-Requested-With=XMLHttpRequest

Notice there is this extra header in there:

x-requested-with: XMLHttpRequest

This extra header was inserted via JavaScript and indicates that the request was made via the XMLHttpRequest object. This enables the server to distinguish between a normal request and an AJAX request because a normal browser request would not contain that header. This is a de facto standard used by many JavaScript frameworks such as Dojo, jQuery, and Prototype.js.

If you look carefully at the contents of the preceding request, you'll notice that the last line of the request contains the contents of the form post. More interestingly, the form contents contain the X-Requested-With header value.

query=Hello%20Ajax!&X-Requested-With=XMLHttpRequest

This is a flag that was auto-set by the function in MicrosoftMvcAjax.js, effectively "injecting" the value into the submitted form. Why does it repeat this information here?

It turns out, this was because of a lesson learned from the ASP.NET team with the UpdatePanel in which some proxies and firewalls on the Internet would strip out headers they didn't recognize. Technically, this is bad behavior on the part of these proxies, but that doesn't really matter to the end user if it doesn't work. The workaround was to move these values to the body of the form post. The AJAX helpers in ASP.NET MVC do both to comply with standards set by other JavaScript frameworks, while at the same time dealing with these bad proxies.

So with this header in tow, it's possible to check for an AJAX request like so:

public ActionResult HelloAjax(string query){    //make sure this is an Asynch post    string isAjaxPost = Request.Form["X-Requested-With"] ??      Request.Headers["X-Requested-With"];    if (!String.IsNullOrEmpty(isAjaxPost))    {        return Content("You entered: "+ query);    }    else    {        return RedirectToAction("Index", new { query = query });    }}

This works nicely, and sends the user back to the Index action, appending the query string. From there, you can run a quick check and output the results as needed.

This isn't optimal, however, since checking for the magic string X-Requested-With is a bit much to ask of anyone. The ASP.NET MVC team thought of this as well, and created an Extension Method within the System.Web.Mvc namespace, which extends HttpRequestBase to do this check.

And now you can do away with the cumbersome form checking and header checking code, and replace it with a call to Request.IsAjaxRequest within your controller. This now changes your Controller code to:

public ActionResult HelloAjax(string query){    //make sure this is an Asynch post    if (Request.IsAjaxRequest())    {        return Content("You entered: "+ query);    }    else    {        return RedirectToAction("Index", new { query = query });    }}

which is a lot nicer. But we're not done with this action yet, as there's something left that could use some cleaning up. Redirects "are from the devil" as one of the authors would say, and this situation can be handled a bit more elegantly. Let's take a look at some alternatives.

At the core of it, what you're trying to do here is to return a chunk of HTML if scripting is enabled (let's say it's a tabular result set) and a bigger chunk of HTML if it's not enabled (the same tabular result set, surrounded by the page HTML). Given this, you can work with some of the framework's tools to accomplish this.

Using Partials for Rendering

Originally, Chapter 6 discussed Partials as a great way to keep your Views DRY, and they are very useful when working with AJAX as well. To illustrate this, change the example you're working with slightly and create a new Action and View for searching products in the Northwind database. As with all Northwind samples, this will be exceedingly simple but, hopefully, illustrate the point we are after.

The View will have a form that allows a user to submit a basic search term. Use AJAX for this, and allow the results to be returned to a DIV element with the ID "results":

<script src="/Scripts/MicrosoftAjax.js" type=" text/javascript"></script><script src="/Scripts/MicrosoftMVCAjax.js" type=" text/javascript"></script><%using (Ajax.BeginForm("ProductSearch",         new AjaxOptions { UpdateTargetId = "results" }))     { %>       <%=Html.TextBox("query",null, new {size=40}) %>       <input type=" submit" />   <%} %>    <div id=" results">    </div>

This form posts to the ProductSearch action, which uses LINQ To SQL to search the product results for Northwind:

public ActionResult ProductSearch(string query){IList<Product> products = new List<Product>();if(!String.IsNullOrEmpty(query)){NorthwindDataContext db = new NorthwindDataContext();products = (from p in db.Products            where p.ProductName.StartsWith(query)            select p).ToList();}return View(products);}

If you're familiar with AJAX programming, you see a problem here. This action returns the full view, and if you post this (and scripting is enabled), the return set will be a full rendering of the View, and the page will be very ugly indeed, as you can see in Figure 7-3.

Image from book
Figure 7-3

What you need to do in this case is create a Partial (a sub-View if you will) that only renders the search results:

<%if(ViewData.Model.Count>0){ %><table cellpadding="5"><tr>       <td><b>Product</b></td>       <td><b>Price</b></td></tr><%foreach (MVCAjax.Models.Product p in ViewData.Model)  { %><tr>       <td><%= Html.Encode(p.ProductName) %></td>       <td><%= p.UnitPrice %></td></tr><%} %></table><%} %>

This Partial is simply a ViewPage (typed using IList<Product>) that is stored in the /shared folder in the /views directory — the authors are calling this one "ProductSearchResults.aspx" (you can also use a ViewUserControl if you like — it doesn't matter as you'll see in a second).

With this Partial in place, you can change your Action's return value to correspond to an AJAX request:

IList<Product> products = new List<Product>();if(!String.IsNullOrEmpty(query)){NorthwindDataContext db = new NorthwindDataContext();var products = from p in db.Products                where p.ProductName.StartsWith(query)                select p).ToList();}if(Request.IsAjaxRequest()){return View("ProductSearchResults", products);}else{return View(products);}

Notice that you don't need to tell ASP.NET MVC to look for the View in the /shared folder — that's done for you and is "part of the checkdown" when looking for a view to render. Notice also that you don't need to specify the file extension — indeed a very handy feature from the MVC team!

This works exactly as you would expect (Figure 7-4).

Image from book
Figure 7-4

But unfortunately we're not done yet. The authors still haven't covered the scenario where a user has turned off scripting. This part's very simple, however, and it takes one line of code in your View:

<div id=" results"><%Html.RenderPartial("ProductSearchResults", ViewData.Model); %></div>

This will render the results when the full View is rendered (in other words when scripting is turnedoff) and not just the Partial.

Some Things You May Not Know About Microsoft ASP.NET AJAX

There are a lot of opinions about Microsoft ASP.NET AJAX, some good, some bad, and many in between. When you think of this AJAX framework, what springs to mind? Drag and drop demos? ScriptManagers and UpdatePanels? These, in fact, are derivatives of a much larger work — one that many people don't even know exists.

The authors may as well get this out of the way: Microsoft ASP.NET AJAX is not a bloated framework. The term bloated is often thrown at frameworks or technologies that either:

  • People don't understand or aren't completely aware of and don't want to take the time to learn

  • Come from a provider people don't respect or like

  • People have heard bad things about

  • Are actually bloated

You might be wondering "why is this chapter starting this way" and the answer is, that in our experience, people don't have the best opinion of the Microsoft ASP.NET AJAX framework (suggesting it's bloated, etc.) and that's unfortunate.

The size of the runtime is actually 82Kb — the size of an average image and roughly the same size as its jQuery brother. The debug version (which should not be used "live") is, by contrast, 250Kb — which is what many people might be founding their opinion on.

The perception issue might also resolve down to the fact that the focus has been, for the most part, on the ASP.NET AJAX Control Toolkit (which uses the ASP.NET AJAX library for its functionality), which offers a massive amount of functionality. It's not bloated either — but the amount of functionality present in the library can lead people to believe that it's just too darn big.

Something to consider, when it comes to UI frameworks from Microsoft, is that Microsoft developers (speaking generally) tend to rely on UI code encapsulation in the form of server controls, which do a lot of the JavaScript work for you. JavaScript gets a bad rap in this case, with many web developers opting to sweep it under the server control rug.

Again, this is unfortunate because the Microsoft ASP.NET AJAX framework is very nimble and allows you to do some very cool things in the browser — on your own and the way you like it (which, coincidentally, is the focus of ASP.NET MVC).

Given these things, it's easy to see why the Microsoft ASP.NET AJAX library has gone largely overlooked. It's time to change that — and we hope this chapter will open your eyes to the neat things you can do with this lightweight, powerful library.

How it Works

The design approach behind Microsoft ASP.NET AJAX can best be thought of as ".NET for JavaScript" — it's that simple. Core concepts in JavaScript (such as strings and numbers) become objects that have properties similar to those of their C#/VB counterparts. From MSDN:

"The Microsoft AJAX Library adds a type system and extensions to JavaScript objects to provide frequently used object-oriented features that resemble features in the .NET Framework. They enable you to write AJAX-enabled ASP.NET applications in a structured way that improves maintainability, makes it easier to add features, and makes it easier to layer functionality. Microsoft AJAX Library extensions add the following capabilities to JavaScript:

  • Classes

  • Namespaces

  • Inheritance

  • Interfaces

  • Enumerations

  • Reflection

The library also provides helper functions for strings and arrays."

This approach is quite expansive, as you can imagine, and the fact that it fits into an 80Kb file is quite impressive.

Much of the criticism of JavaScript as a language is its "drive-by coding style" — which means that you can spackle your web pages with bits of JavaScript here and there to achieve the desired functionality and easily end up with an awful mess that's very hard to debug and support.

Libraries and frameworks (like ASP.NET AJAX and jQuery) are trying to change this. As you'll see in the following pages, these two libraries have some overlap, but their focus is actually quite different. jQuery focuses on providing shortcuts to common solutions (which the next section will talk more about), and ASP.NET AJAX provides a foundation for you to build upon.

Thankfully, the builders of ASP.NET AJAX decided to mirror the .NET Framework as closely as they could — so creating solutions with ASP.NET AJAX as the foundation is remarkably like coding in C#/VB against the CLR. So similar, in fact, that it didn't take long before someone decided to create a tool that takes C# and translates it into JavaScript working with ASP.NET AJAX.

Script #

In May of 2006, Nikhil Kothari of Microsoft shared a tool that he created as a "spare time project." This tool, in short, compiles C# code into JavaScript rather than MSIL (binary bits). Or, as Nikhil describes it:

"Script# brings the C# developer experience (programming and tooling) to JavaScript/Ajax world"

Another way to put this is that you can, if you want to, replace your entire server-side code experience with JavaScript as your code-behind. This might at first sound absolutely crazy — however, if you consider the advent of Rich Internet Applications and that more and more capabilities are being pushed the browser — well this doesn't sound like a lot of "crazy talk" anymore.

Nikhil has created pretty detailed project support for Script# — including debugging and a "Code-Behind" generator for Script#-enabled web pages. The authors aren't going to go into any of the higher-end stuff such as this, however because it relies a bit more on the Web Forms model in order to work properly.

Note 

Diving further into Script# is a bit beyond the scope of this book, but if you're interested in it you'll definitely want to check out Nikhil's blog at http://projects.nikhilk.net/ScriptSharp for more information.

Updating an HTML Element When Submitting a Form

The example in the last section shows how to do just this with Microsoft ASP.NET AJAX — so let's take a look at how to do this with jQuery. For this example (and all of the others), you'll be using jQuery 1.2.6.

One of the cool things about jQuery is the way people have built plug-ins on top of it — allowing you to do just about anything you need to do without writing a ton of code. For this example, you'll use the popular jQuery Form plug-in, that turns your Form into an AJAX form with very little code. You don't need to use this plug-in to get AJAX functionality out of jQuery — but it does save a good amount of time.

jQuery works by "wiring" up HTML elements and their events using jQuery's selector API. To select an item, you use the selector API to find it:

$("myelement").focus();

This searches the page's DOM for a tag with the name "myelement" and set the cursor focus on it (if applicable). You can search in any number of ways — by CSS class, element ID, and so on.

Note 

The authors aren't going to discuss all of the goodness of jQuery here (it would be way too easy to get off track), and if you'd like to know more, head over to http://jquery.com.

To set up your page to work with jQuery, start by "wiring up" the HTML element you're interested in. The authors have redone the HTML here just a bit for the sake of clarity, and also to give the elements some IDs that jQuery can work with:

<h1>Product Search - jQuery</h1><form action="<%=Url.Action("ProductSearch") %>" method=" post" id=" jform">       <%= Html.TextBox("query", null, new {size=40}) %>       <input type=" submit" id=" jsubmit" value=" go" /></form><div id=" results2">     <%Html.RenderPartial("ProductSearchResults", ViewData.Model); %></div>

In addition, a reference has been added to the Excellent jQuery.form plug-in that allows you to submit forms using AJAX. There are many jQuery plug-ins that allow you to do this — jQuery.form is the one chosen for this example.

The next step is to "override" the submit method of your form (which has been given the id of "jform"). After adding script references to jQuery and jQuery Form (your addin), this script is added inside the page's head tag:

<script src="/Scripts/jquery-1.3.2.js" type=" text/javascript"></script><script src="/Scripts/jquery-form.js" type=" text/javascript"></script><script type=" text/javascript">$(document).ready(function() {$(‘#jform').submit(function() {        $(‘#jform').ajaxSubmit({ target: ‘#results2') });        return false;});});</script>

The first line here is jQuery's standard "main" method — it fires when the document has loaded all of the HTML elements (also known as "body.onload").

The next line "overrides" the submit method on the form and invokes another method instead — the ajaxSubmit method from the jQuery Form library. The ajaxSubmit method magically takes the values from all of the form elements, wraps them in an XMLHttpRequest, and posts them to the form's action URL.

In this code you've also specified a "target" option, which tells jQuery that you want the results of the asynchronous form post to be rendered inside the target element. For some additional options include, see the following table.

Open table as spreadsheet

Option

Description

url

Overrides the form's action and submits the form to a URL that you set.

type

Sets the submit method of the form (POST or GET).

beforeSubmit

A function that is called before the form is submitted. This can be used to validate the submitted data or to perform some pre-submission logic. The arguments that are passed to this form are data (an array of posted data), the form element, and the options set.

Success

A callback function that's called when the form successfully posts. This function is passed the response text as well as the response status.

dataType

The type of data that is expected from the response. The default is null, but you can also set "json", "xml", and "script".

resetForm

Indicates whether the form data should be reset on success.

clearForm

Indicates whether the form data should be cleared on success.

The good news at this point is that the flag we checked in the earlier example (Request.IsAjaxRequest) will work perfectly with jQuery, just as it does with the AJAX form helpers. This is because the same flag ("X-Requested-With") is used for both scenarios: the AJAX form helpers and jQuery.

No changes to the controller are necessary — everything just works!

The Auto-Complete Text Box

The auto-complete textbox is, to most people, the prototypical Web 2.0 AJAX tool. No AJAXified site would be complete without it! Happily, this is quite simple to implement using the two JavaScript libraries. For this, you'll need a database, and as always you'll lean on good old Northwind to help you out. Let's add some spice to the search page and put some auto-complete goodness in place for the search functionality.

Important 

Product Team Aside

This is one of those situations where many people looking at this example will groan heavily and wonder how the authors even tie our shoes in the morning — let alone pass the interview loop at Microsoft. It goes without saying that opening a database connection at the whim of a keypress is not a good idea (usually), and we understand that. Unfortunately, we need to show you how to use the code, and, in most cases, the example gets completely lost when you start discussing implementation details. So we ask for your patience and ask you to read between the lines of code here. To clear our names, we should mention here and now that you will want to implement some type of cached result set to avoid excessive data connections in this case.

Implementing Auto-Complete with Microsoft ASP.NET AJAX

You're going to implement the same logic here as you did with jQuery in the previous example: if it's been written already, use it. No need to create your own JavaScript! In this case, you can take many of the files already created for the ASP.NET AJAX Toolkit and implement them for your own needs so that you don't need to write a whole mess of code (which really isn't all that much — this is just nicer because it handles the situation for you):

  1. The first thing to do is to go get the ASP.NET AJAX Control Toolkit files from CodePlex:

  2. When you're there, you want to be sure you download the client-file-only version — you don't need (because you can't use) any of the server controls that come with the Toolkit. Once they are downloaded, take a look at the files that come with the Toolkit — there are a lot of them! The good news is that you don't need that many — just a few of them to get running with.

  3. Next, you need to implement the routine on the server that will fetch the list that we want to see. The first thing to tackle here is the server code. For this, use a SOAP web service because it's very easy to set up, and it's also required by the tool you're going to use: the ASP.NET AJAX AutoComplete control. The code isn't anything special — it just looks up a value in the Northwind Products table based on partial name of the Product:

    /// <summary>/// Summary description for ProductService/// </summary>[WebService(Namespace = "http://tempuri.org/")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)][System.ComponentModel.ToolboxItem(false)][System.Web.Script.Services.ScriptService]public class ProductService : System.Web.Services.WebService{    [WebMethod]    public string[] ProductNameSearch(string prefixText, int count)    {        NorthwindDataContext db = new NorthwindDataContext();        string[] products = (from p in db.Products                             where p.ProductName.StartsWith(prefixText)                    select p.ProductName).Take(count).ToArray();        return products;    }}

    This is a very typical web service method, with one thing to note, which is this Attribute setting:

    [System.Web.Script.Services.ScriptService]

    This identifies the method as callable from script on a web page. This is not enabled by default (for security reasons), and you must make sure you set it (there's even a comment provided for you by the template) or else the code will not work.

  4. The next thing that needs to happen is the adding and referencing of the scripts required to run the control. You can find each of these in the ASP.NET AJAX Control Toolkit source code (see Figure 7-5):

    At first it may seem a little "cumbersome" to require this many scripts for some fairly basic functionality. One thing to keep in mind, however, is that the ASP.NET AJAX Control Toolkit is very, very modular, and each of these scripts is quite small. As you add more AJAX functionality to your site, their reuse will grow — keeping everything nice and DRY.

  5. Next thing you need to do is to add references to these scripts and to "wire up" the control:

    <script src="/Scripts/AjaxControlToolkit.ExtenderBase.BaseScripts.js"        type=" text/javascript"></script><script src="/Scripts/AjaxControlToolkit.Common.Common.js"        type=" text/javascript"></script><script src="/Scripts/AjaxControlToolkit.Animation.Animations.js"        type=" text/javascript"></script><script src="/Scripts/AjaxControlToolkit.Animation.AnimationBehavior.js"        type=" text/javascript"></script><script src="/Scripts/AjaxControlToolkit.PopupExtender.PopupBehavior.js"        type=" text/javascript"></script><script src="/Scripts/AjaxControlToolkit.Compat.Timer.Timer.js"        type=" text/javascript"></script><script src="/Scripts/AjaxControlToolkit.AutoComplete.AutoCompleteBehavior.js"        type=" text/javascript"></script><script type=" text/javascript">    Sys.Application.add_init(function() {    $create(        AjaxControlToolkit.AutoCompleteBehavior, {            serviceMethod: ‘ProductNameSearch',            servicePath: ‘/ProductService.asmx',            minimumPrefixLength: 1,            completionSetCount: 10        },        null,        null,        $get(‘query')    });</script>

    It's very important to register these scripts in the right order, as they depend on each other. Unfortunately, the order in which you lay out the <script> tags dictates when the JavaScript will become available to the browser (this is a browser limitation, not a platform one).

    The neat thing about the ASP.NET AJAX Control Toolkit is that it's very object-oriented. Notice that the code here works directly against a "Behavior" — in this case, the AutoCompleteBehavior — and allows you set properties on this Behavior as needed (such as the web service method to call, the web service path, and the controls to use for input/output).

  6. Once this code is in place, you can use it immediately, as shown in Figure 7-6:

Image from book
Figure 7-5
Image from book
Figure 7-6

As you can see, wiring up the ASP.NET AJAX Control was not difficult in the least. There are a lot of goodies in the toolkit that can be reused with ASP.NET MVC. If you want to use any of the items from the toolkit, all you need to do is:

  • Figure out which script files are required

  • Figure out which method needs to be invoked

  • Add the scripts to your page and wire up the control

This may seem like a lot guesswork, but it's really not. You can simply create a Web Form, add a ScriptManager control to the page and the control that you want to use, run the page, and then view the source. You will see all the wiring that you need.

Or you can head over to Stephen Walther's blog (http://weblogs.asp.net/StephenWalther) and read up on many of the ASP.NET AJAX samples he's put together for ASP.NET MVC.

Filtering Data with a Selectbox

For this example it's jQuery's turn. Let's say that you wanted to round out your super-AJAXy Product lookup search page with some dropdown filtering based on Category. This is quite common on many websites that need to display a lot of data, and we should point out here that you can use this technique with any type of list or selector. Follow these steps:

  1. First, you need to create a new Action that will filter the data and render your partial for you. Be sure to use the AJAX "sniffer" as well to test if this is an asynchronous call:

    public ActionResult ProductByCategory(int id){    NorthwindDataContext db = new NorthwindDataContext();    IList<Product> products = (from p in db.Products                where p.CategoryID == id                select p).ToList();    if (Request.IsAjaxRequest())    {        return View("ProductSearchResults", products);    }else{        return View("ProductSearch", products);    }}
  2. Next, you need to create a SelectList and DropDown to display the Category data. To do, add a couple of lines to the controller code:

    var categories = db.Categories;ViewData["CategoryID"] = new SelectList(categories, "CategoryID","CategoryName");
  3. This code is setting up a SelectList and adding it to the ViewData. You can fetch this data and leverage some "convention" in the framework by using the same name for the output in the View:

    <%= Html.DropDownList("CategoryID"    )%>

    This creates a dropdown list with the ID "CategoryID" that you can now hook up to jQuery:

    <script language=" javascript" type=" text/javascript"> $(document).ready(function() {   $("#CategoryID").change(function() {     var selection = $("#CategoryID").val();     $("#results").load("/home/ProductByCategory/"+ selection);   }}) });</script>

In this script, you'll find the CategoryID dropdown and wire up the onchange event (although you could have done it with the click event of a button) and pull out the selected value, using jQuery's val method. Finally, you're using jQuery's load method, which sets the inner HTML of a page element to the return value of a URL. Your URL, in this case, is that of the new Action: ProductByCategory.

That's all the code that's needed, and it works perfectly (see Figure 7-7).

Image from book
Figure 7-7

Hey, this stuff is pretty easy!

The Modal Popup with jQuery

Modal popup windows have become quite popular on the Web these days, as they offer the ability to "quickly" input or view information pertaining to the page you're looking at. This can be very handy if you don't want the user to be able to access the current page's information.

For example, Community Server (an ASP.NET forums application) gives users the ability to do a "Quick Reply," which pops up a modal dialog with a rich-text box (see Figure 7-8).

Image from book
Figure 7-8

The nice thing about modal dialogs is that they are chunks of reusable functionality that keep the "notion" of the page alive in a user's mind, and most of the time they can be moved around so that the user can view the underlying page for reference.

For this functionality, you'll use jQuery one more time, because there is already a plethora of support for modal dialogs using the jQuery core. The plug-in you'll use for this example is jqModal (available from http://dev.iceburg.net/jquery/jqModal) because it's very lightweight and simple. If you don't like it, however, there are plenty more, including:

A simple web search will probably yield four or five more that you might want to consider as well!

The Modal Popup Code

For this demo, we have downloaded the jqModal code and added it to our /Scripts folder on our ASP.NET MVC site. As with most things jQuery, this plug-in works by flexing class and naming conventions. The HTML is very self-explanatory in terms of what it's doing, and to get the modal window to open, we simply reference the script files and assign CSS styling to elements that we want to do certain things:

<script src="/Scripts/jquery-1.3.2.js" type=" text/javascript"></script><script src="/Scripts/jqModal.js" type=" text/javascript"></script><link href="/Scripts/jqModal.css" rel=" stylesheet" type=" text/css" /><script type=" text/javascript">    $().ready(function() {        $(‘#dialog'.jqm();    });</script><button class="jqModal ">Click Me!</button><div class="jqmWindow " id=" dialog">    <a href="#" class="jqmClose ">Close</a>    <hr>        <h1>My Modal Box!</h2>        Hi! I'm a modal dialog!</div>

In typical jQuery fashion, the code to get this to work is exceedingly minimal. The actual "window" that will be displayed is actually a <div> element with the class "jqmWindow". Everything that's inside that window is displayed as it's laid out inside the <div>.

In addition, there is an <a> tag inside the modal <div> that has a class of "jqmClose". Clicking this element (it can be a button or any clickable item) will close the modal window (clicking outside of the window will close it as well).

The glue that holds all this rich client goodness together is the initial wiring in the ready function at the top of the page. This simply "wires" the element with the ID of "dialog" to the jqModal system — identifying it as the modal window.

You can also show remote URLs, which is nice when using something like form input, for example:

<script src="/Scripts/jquery-1.3.2.js" type=" text/javascript"></script><script src="/Scripts/jqModal.js" type=" text/javascript"></script><link href="/Scripts/jqModal.css" rel=" stylesheet" type=" text/css" /><script type=" text/javascript">    $().ready(function() {       $(‘#dialog').jqm({ ajax: ‘/Remote.htm', trigger: ‘a.trigger' });    });</script><a href="#" class="trigger ">Click ME!</a><div class="jqmWindow " id=" dialog">    Loading...</div>

This code works much in the same fashion, except that you pass values into the jqm function that denote which page to open as well as which element on the page triggers the opening (again, using class names as a convention).

This works quite nicely.

jQModal comes with a some nice styling options as well (the authors are just showing the defaults here), and there is a lot of flexibility in terms of how things look. And as always with jQuery, if this plug-in doesn't fit the bill, there are probably 10 more out there waiting for you to find .

The Rating Control

A lot of sites like to use some form of rating control so that users can share their opinions on what's being displayed. You can see this kind of thing quite often with ecommerce sites that allow their users to rate the products they sell.

Once again, you'll turn to jQuery because it makes this kind of thing ridiculously simple to implement. And, once again, there are a number of ready-to-go plug-ins that allow to you implement a ratings scheme on your site very quickly. For this demo, we are using the jQuery.rating plug-in created by Fyneworks.com. It's a really lightweight plug-in that works with class names and form inputs to very easily represent the typical "star-rating" scheme.

To get started, head over to www.fyneworks.com/jquery/star-rating and download the plug-in. You're given the scripts you need to get started as well as a top-notch demo page, images, and the CSS you'll need to get rolling quickly.

Once you've added the files to your site (we are putting their images in /Content/images and the scripts and CSS in the /Scripts directory — note that you may need to update the CSS file if you change the location of the default images); it's a matter of writing some very quick HTML:

<script src="/Scripts/jquery-1.3.2.js" type=" text/javascript"></script><script src="/Scripts/jquery.MetaData.js" type=" text/javascript"></script><script src="/Scripts/jquery.rating.js" type=" text/javascript"></script><link href="/Scripts/jquery.rating.css" rel=" stylesheet" type=" text/css" /><input name=" rating" type=" radio" class="star " value="1"/><input name=" rating" type=" radio" class="star " value="2"/><input name=" rating" type=" radio" class="star " value="3"/><input name=" rating" type=" radio" class="star " value="4"/><input name=" rating" type=" radio" class="star " value="5"/>

This is where jQuery.rating shows its cleverness (and also shows off the simplicity by which jQuery operates). All you have to do is plug in the jquery.rating.css class and set the class of the radio buttons to "star", and you're good to go (see Figure 7-9).

Image from book
Figure 7-9

That, Dear Reader, is pretty darn simple. What has effectively happened here is that jQuery.rating has taken the radio buttons and effectively masked their looks and behavior to be that of a star rating system. They are still very much form inputs, so if you select four stars in the rating above you could post that back to the server as you would any radio button.

To illustrate this, let's try sprucing this code up a bit and adding some functionality to the Controller to read the posted data and send some information to the View using ViewData:

public ActionResult Rating(int? rating) {ViewData["message"] = "Nothing selected";ViewData["rating"] = 0;if (rating.HasValue) {    ViewData["rating"] = rating;    ViewData["message"] = "You selected "+rating;}return View();}

This simple example will read in whatever is posted to it (if anything), and report it back to the View, using the ViewData. Next, expand the View code to make it a bit more functional:

<%  int selectedRating = (ViewData["Rating"] as int?) ?? 0;%><form action="/home/rating" method=" post">  <%for (int i = 1; i <= 5; i++) { %>    <input name=" rating" type=" radio" class="star " value="<%=i%>"    <%if(i<=selectedRating){ %> checked=" checked" <%}%>/>  <%} %>  <input type=" submit" value=" go" /></form><br /><%= Html.Encode(ViewData["Message"]) %>
Note 

We realize that some delicate eyes out there may be burning right now seeing this much logic in the View — however, we ask you to squint just a bit longer, as it goes without saying that this functionality absolutely must get put into a helper for reuse. The reason we're putting it all on the page here is to show you everything in one spot. Please forgive us — we know this is geek heresy.

As you may have suspected, if you set the radio button to "checked", the star that represents it will be filled in as selected (in this case, it's red as opposed to yellow when you hover over it). Therefore, the authors have added a for loop to output the rating code that we want to show, and we are evaluating if the value in ViewData["rating"] is greater than the current rating index. If it is — we output a "checked='checked'" attribute so that it shows up correctly.

We've also added a Submit button so that we can post back to the server. Selecting four stars and submitting this to the server results in Figure 7-10.

Image from book
Figure 7-10

But that's not very AJAXy is it! What about auto-submitting the results Web 2.0-style so that you can do this experience up right! This is pretty simple to wire up, and to make it happen you'll use one of the plug-ins you used previously: jQuery-form. To work with jQuery-form, simply add a script reference to the page, right below the reference to jQuery:

<script src="/Scripts/jquery-form.js" type=" text/javascript"></script>

Next, wire up the rating control's callback function — the thing that is called when the control is selected. To do this, use jQuery's "wiring" and set up the rating control to submit the form when a value is set (also add an ID to your form, and call it "jform"):

<script language=" javascript" type=" text/javascript">$().ready(function() {$(‘.star').rating({callback: function(value, link) { $("#jform").ajaxSubmit();}});});</script>

Note that this is where the two plug-ins will work together — the rating control "OnRate" (for lack of better words) sets a callback function, which in this case is executing an ajaxSubmit (see above for more information) and sending the results off to the server.

Doing it this way allows you to remove the submit button and also the confirmation message (you don't need it anymore because you're not leaving the page):

<%int selectedRating = (ViewData["Rating"] as int?) ?? 0;%><form action="/home/rating" method=" post" id=" jform">  <%for (int i = 1; i <= 5; i++) { %>    <input name=" rating" type=" radio" class="star " value="<%=i%>"    <%if(i<=selectedRating){ %> checked=" checked" <%}%>/>  <%} %></form><br />

Putting all this goodness together, you now have a very cool AJAX-enabled star-rating control that posts back to the server (see Figure 7-11).

Image from book
Figure 7-11
 
 

Summary

As you can tell, there are a lot of choices for enabling client-rich functionality for your applications. ASP.NET MVC's goal is to stay out of your way, and if you've noticed anything in these examples, hopefully, it's how little the framework needs to know about what you're doing on the client, as well as how little effort is required on your part to harness the script elements to the UI.

There are many more examples that we could show here — as well as many more frameworks! Hopefully, you've come away from this chapter with an appreciation of the alternatives that are out there — and also the frameworks that are available to you, which, hopefully, will keep you from having to break out the glue and spackle, injecting bits of JavaScript here and there all over your application.

posted on 2010-06-26 13:26  Yan-Feng  阅读(672)  评论(0编辑  收藏  举报