AJAX(Professional ASP.NET MVC 3
[翻译]《ASP.NET MVC 3 高级编程》第八章:AJAX(Professional ASP.NET MVC 3 --- Chapter 9: AJAX)
关注焦点
所有你想知道的 jQuery Ajax Helper 深入了解客户端验证 使用 jQuery 插件现在很少见有 Web 应用不使用 AJAX 技术的。 AJAX 是 Asynchronous JavaScript and XML 的缩写。在实践中, AJAX 主张使用一切技术来构建最佳用户体验的 Web 应用程序。 在实际使用中,使用到了一些异步通信,然后再响应时,再辅助一些有趣的动画和颜色的变化。如果你可以为用户提供更好的应用程序的用户体验,让他们可以更高效的工作,他们会更加爱你!
ASP.NET MVC 3 是一个现代的 Web 框架,像每一个现代的 Web 框架一样,从一开始就会有支持 AJAX 的责任。其支持 AJAX 的核心来自于 jQuery 的 JavaScript 库。所有 ASP.NET MVC3 的 AJAX 功能都是建立并扩展自 jQuery 的功能。
要想明白 ASP.NET MVC3 中的 AJAX 功能怎么使用,就必须先从 jQuery 开始入手。
jQuery
jQuery 的口号是“少写,多做”,口号完美的描述了 jQuery 的体验。 API 简洁,但是功能强大,库本身灵活而轻量级。最重要的是, jQuery 支持所有现代浏览器(包括 IE 、 Firefox 、 Safari 、 Opera 和 Chrome ),并隐藏了很多不一致的接口(和错误),您可能会遇到针对不同的浏览器提供不同的 API 而提供不同的代码。使用 jQuery ,不仅写更少的代码,并能在更短的时间内完成工作,并且也能保证头发始终保持在头顶上。
jQuery 是目前最流行的 JavaScript 库之一,并且依然还是一个开源项目。在 jQuery.com 网站上你可以找到并下载最新版本的库、文档和插件。你可以在 ASP.NET MVC 应用程序中找到 jQuery 。微软支持 jQuery ,当你创建一个新的 MVC 项目时, ASP.NET MVC 项目模板会将您所需的 jQuery 文件放置在 Scripts 文件夹。
在本章中,你会看到在 MVC 框架中使用 jQuery 来实现客户端验证和异步请求等功能。在我们深入了解 ASP.NET MVC 底层功能之前,让我们来快速浏览下 jQuery 的底层功能。
jQuery 特点
jQuery 擅长查找、遍历和操作 HTML 文档内的 HTML 元素。一旦你找到一个元素,使用 jQuery 可以很容易的操作元素的事件处理程序,元素动画以及和其他元素进行 AJAX 交互。本节将从 jQuery 函数开始来探寻 jQuery 的功能。
jQuery 函数
jQuery 函数是对象,你可以通过这个对象来访问 jQuery 的功能。开发人员在开始使用 jQuery 时会为此而感到困惑。感到困惑也同样是因为函数( jQuery )的别名 $ 符号(因为这样可以在调用 jQuery 功能时可以大量减少字符输入)。更令人困惑的是 $ 机会可以接受任意类型的参数,而依据此推断出你打算调用的功能。下面是一些 jQuery 函数代码的典型使用方法:
$(function () {
$( “ #album-list img ” ).mouseover(function () {
$(this).animate({ height: ‘ +=25 ’ , ‘ +=25 ’ })
.animate({ height: ‘ -=25 ’ , ‘ -=25 ’ });
});
});
代码的第一行调用了 jQuery 函数( $ ),传递给匿名 JavaScript 函数一个参数。
$(function () {
$( “ #album-list img ” ).mouseover(function () {
$(this).animate({ height: ‘ +=25 ’ , ‘ +=25 ’ })
.animate({ height: ‘ -=25 ’ , ‘ -=25 ’ });
});
});
jQuery 会假定你提供了一个方法,并在浏览器从服务器端加载完成 HTML ( DOM )信息并建立完成文档对象模型时立即执行。因为在这个时候,你就可以安全而完善的执行 DOM 有关的脚本了。
第二行代码向 jQuery 函数传递了字符串参数“ #album-list img ”:
$(function () {
$( “ #album-list img ” ).mouseover(function () {
$(this).animate({ height: ‘ +=25 ’ , ‘ +=25 ’ })
.animate({ height: ‘ -=25 ’ , ‘ -=25 ’ });
});
});
jQuery 将会把这个字符串调用解释为 选择器 。选择器会告诉 jQuery 应该在 DOM 中查找一个什么样的元素。你可以获取这个元素的属性值, class 的值,相对位置,以及其它信息。第二行的选择器告诉 jQuery 在这个元素的范围内查找所有 ID 值为“ album-list ”的 img 元素。
当选择器执行完成时,它会返回匹配到的一个或多个元素的对象。你可以通过 jQuery 的一些额外的方法来调用操作包装的集合中所有的元素。例如,可以为所有由此选择器匹配到的图片添加鼠标悬停事件处理程序。
jQuery 利用很好的利用了 JavaScript 的函数编程的能力。你会发现可以将自己创造的函数作为参数传递给 jQuery 的方法。例如,定义 mouseover 方法,如果不与 onmouseover 事件相关联,无论如何浏览器都不会知道该怎么去触发这个方法。为了在事件触发时触发,你需要为事件传递处理的函数代码:
$(function () {
$( “ #album-list img ” ).mouseover(function () {
$(this).animate({ height: ‘ +=25 ’ , ‘ +=25 ’ })
.animate({ height: ‘ -=25 ’ , ‘ -=25 ’ });
});
});
前面的例子中,当触发元素的 mouseover 事件时会执行 animates 方法。而出发当前动画的这个元素被引用为 this (出发事件的元素)关键字。请注意,代码是通过向 jQuery 方法中传递 this 关键字( $(this) )获取的这个元素。 jQuery 的参数视为这个元素的引用然后通过 jQuery 方法进行包装并返回这个元素。
一旦 jQuery 返回了包装好的元素,你可以像上面一样是使用与 animate 类似的方法进行操纵这些元素。在示例代码中,使图片的拉伸(宽和高都增加 25 像素),然后缩小一些(使图片宽和高各减少 25 像素)。
执行这段代码时,当用户将鼠标移动到图片上时,他们会看到个微妙的提醒效果,图片先放大了然后又恢复了,这种是应用程序必须要使用的吗?不!但是简约的效果以及鲜亮的外貌,你的用户会喜欢的。
通过本章的内容,你会看到更多更为实际的功能特性。首先,让我们仔细看看你所需要使用到的 jQuery 功能。
jQuery 选择器
选择器会根据你传入 jQuery 函数的字符串作为条件在 DOM 中搜索可匹配的元素。在前一部分,选择器使用“ #album-list img ”来查找图片标记。你也可以使用层叠样式表( CSS )的方式来查找元素。 jQuery 选择器语法派生自 CSS 3.0 选择器。表 8-1 中罗列了所有 jQuery 选择器支持的代码方式。
示例
含意
${"header"}
查找 id 为 "header" 的元素。
${".editor-label"}
查找所有类名为“ .editor-label ”的元素。
${"div"}
查找所有的 <div> 元素。
${"#header div"}
查找 id 为“ header ”的 <div> 元素。
${"#header >div"}
查找 id 为“ header ”节点的所有 <div> 子元素。
${"a:even"}
查找查找所有奇偶的锚点元素。
表中的最后一行展示了 jQuery 像 CSS 一样支持伪类。使用这个伪类可以选择到偶数或奇数的元素,访问链接你可以查看到一个完整的 CSS 选择器列表:
http://www.w3.org/TR/css3-selectors/ 。
jQuery 事件
jQuery 的另一个强项就是为订阅 DOM 中的事件提供 API 。虽然你也可以使用字符串在通用绑定中指定使用事件的名称, jQuery 还是提供了一些常见的时间,如 click 、 blur 和 submit 等事件专用方法。如前所示,你可以为 jQuery 传递个函数告诉它事件发生时该做什么。函数可以是匿名的,就像你在“ jQuery 函数”一节中所看到的例子,或者你可以指定函数的名称来作为事件处理程序,例如下面的代码:
$( “ #album-list img ” ).mouseover(function () {
animateElement($(this));
});
function animateElement(element) {
element.animate({ height: ‘ +=25 ’ , ‘ +=25 ’ })
.animate({ height: ‘ -=25 ’ , ‘ -=25 ’ });
}
当你要选择 DOM 元素或进行事件处理时, jQuery 可以非常容易的操作页面上的元素。你可以读取或设置属性值,添加或删除元素的 CSS 类以及其他更多操作。下面的代码是当用户将鼠标移动到链接元素上时,添加或删除 CSS 的高亮类。当用户在页面上移动鼠标到锚点标记时会有不同的显示(假设你已经生成了高亮样式)。
$( “ a ” ).mouseover(function () {
$(this).addClass( “ highlight ” );
}).mouseout(function () {
$(this).removeClass( “ highlight ” );
});
关于前面的代码有一些有趣的事情:
所有 jQuery 方法都会生成一个包裹集,如悬停的方法,也会返回相同的 jQuery 的包裹集。这意味着你可以继续调用 jQuery 方法而无需重新选择这些元素。我们将这个称为方法链接。
在 jQuery 中几乎你可以看到所有的常见操作。设置 mouseover 和 mouseout 的共用操作方法,你可以在其中切换不同的样式类。你可以使用一些 jQuery 的快捷方式和最后的代码片段将其重写为如下形式:
$( “ a ” ).hover(function () {
$(this).toggleClass( “ highlight ” );
});
多么有力量的三行代码啊——这就是 jQuery 的美妙之处。
jQuery 和 AJAX
jQuery 可以将你需要的所有内容通过异步请求的方式发送回你的 Web 服务器。你可以发起 POST 请求、 GET 请求或 jQuery 请求在完成时(或发生错误时)通知您。你可以使用 jQuery 发送和接收 XML 数据( AJAX 中 X 代表 XML ),但是在本章中,你会看到你会看到 HTML 片段数据,文本或 JavaScript Object Notation ( JSON )等格式。 jQuery 使得 AJAX 更为容易。
事实上, jQuery 改变了 Web 开发人员编写脚本代码的方式,让很多工作变的更为容易。
不唐突的 JavaScript
在 jQuery 还没有出现的时候, Web 流行将 JavaScript 代码和 HTML 放在同一个文件中。甚至还把 JavaScript 代码当成是 HTML 元素的属性嵌入其中。就如同下面的 onclick 处理程序:
<div onclick= ” javascript:alert( ‘ click ’ ); ” >Testing, testing</div>
可能你在那段日子里面也曾在嵌入标记中写过 JavaScript ,因为当时也再没有什么更简单的方法可以捕获点击事件。嵌入 JavaScript 代码虽然可以工作,但是代码会变得非常凌乱。 jQuery 的出现改变了这种情况,因为你可以使用另外一种更为优秀的方法来找到元素和捕获点击事件。现在你可以从 HTML 的属性中移除 JavaScript 了。事实上,你完全可以将 JavaScript 代码从 HTML 中移除出去。
不唐突的 JavaScript 的作用是保持 JavaScript 代码与 HTML 标记分离。将所有你需要用到的脚本代码打包到 .js 文件中。如果你查看源代码,将不会看到任何 JavaScript 代码。即使是 HTML 渲染代码视图,你也不会看到任何 JavaScript 在里面。而这个页面唯一是用脚本的标记,你会看到一个或多个 <script> 标记引用了 JavaScript 文件。
你可能很快就会发现不唐突 JavaScript 的魅力,因为它同样遵守和推崇 MVC 的设计模式。保持显示标记使其与 JavaScript 所控制的行为相分离。不唐突 JavaScript 还有其他的好处。 JavaScript 代码存储在单独的文件中,浏览器只需要下载一次即可缓存在本地,这样也利于网站的性能提升。
不唐突, JavaScript 允许你的网站采用“逐步增加”的策略来更新内容。而逐步增加则是提供内容的一种重要方式。只有当你的设备或浏览器可以支持脚本以及样式表的功能,这时才能够制作更为先进的内容,例如动画。维基百科对于逐步增加的策略有一个非常好的概述: http://en.wikipedia.org/wiki/Progressive_enhancement 。
ASP.NET MVC3 需要不唐突的 JavaScript 。而不是在做类似于客户端验证的功能时在视图中写 JavaScript 代码,或是框架将代码附着到 HTML 的属性中。使用 jQuery 框架可以很方便的找到和解释元数据,然后使用外部关联的脚本文件为元素附加行为。感谢不唐突的 JavaScript 提供的 AJAX 功能使 ASP.NET MVC 可以支持“逐步增强”。如果用户的浏览器并不支持脚本,您的网站也依然会正常工作(但是可能他不会使用到“好用”的功能,例如客户端验证)。
接下来让我们研究一下如 jQuery 在 MVC 应用程序中如果为动作添加不唐突的 JavaScript 。
使用 jQuery
在 Visual Studio 中使用 ASP.NET MVC 模板创建一个新项目时,项目模板会为你创建你所需要的 jQuery 。每个新项目中都会包含一个 Scripts 文件夹来放置 .js 文件,如图 8-1 所示:
jQuery 库的核心文件被命名为 jquery-< 版本 >.js ,在图片中当前 jQuery 的版本是 1.4.4 。当你打开这个文件时,你会发现这个文件中的代码可读,并且还包含相关的代码注释。
注意,这里还包含一个 jquery-< 版本 >.min.js 文件。这个是一个压缩版本的 JavaScript ,其中“ .min ”是用来区分未压缩版本的(通常压缩版本会小于默认版本的一半)。它不包含任何不必要的空格字符,没有注释,局部变量名称也被缩短为一个字符长度。如果你打开一个压缩过的文件,你会发现一大堆比可读的 JavaScript 代码。你可以将压缩过的 JavaScript 给你认为是专家的 JavaScript 程序员。问他,他认为这些代码能做什么?
压缩后的文件与未压缩文件在客户端执行的功能是相同的。然而,因为压缩使文件变小,浏览器从服务器下载的字节数减少了,加载和运行的速度也更快。 MVC 应用程序在默认布局视图模板( _Layout.cshtml )中已经使用脚本标记引用了 jQuery 的压缩版本:
<script src= ” @Url.Content( “ ~/Scripts/jquery-1.4.4.min.js ” ) ” type= ” text/javascript ” >
</script>
在布局视图中将上面的脚本标记放置在您的标记之前,接下来你就可以开始使用 jQuery 了。
自定义脚本
当你写自定义 JavaScript 代码时,你可以在脚本目录中新建文件,并将代码保存到新文件中(除非你想在视图中写嵌入式脚本代码,但是如果你这样你就会失去 25 Karma Points 【译者注:我真不知道这是什么,但是参照 google 有一个页面应该是某种等值货币之类的计量单位?】)。例如,你可以从本章开始的将所有的代码都放置到脚本目录的 MusicScripts.js 文件中。 MusicScripts.js 文件内容如下:
/// <reference path= ” jquery-1.4.4.js ” />
$(function () {
$( “ #album-list img ” ).mouseover(function () {
$(this).animate({ height: ‘ +=25 ’ , ‘ +=25 ’ })
.animate({ height: ‘ -=25 ’ , ‘ -=25 ’ });
});
});
顶部注释掉的引用代码并不会在运行时对脚本产生任何影响。参考的唯一目的就让 Visual Studio 知道您会使用到 jQuery 并由 Visual Studio 提供 jQuery API 的代码提自动提示。
要为应用程序添加 MusicScript.js 就需要另外一个脚本标记。脚本标记必须出现在 jQuery 脚本标记之后,因为 MusicScripts.js 会应用到 jQuery ,而浏览器会根据脚本在文档中出现的顺序进行加载。如果你的脚本包含了整个应用程序都会使用到的功能,你可以将其加在 _Layout 视图的 jQuery 脚本标记之后。在这个例子中,你只需要在应用程序的首页使用到这个脚本,所有你可以在 HomeChontroller 的 Index 视图中添加它(视图引擎会在加载完 jQuery 脚本标记之后就开始渲染页面的 body 部分)。
<div id= ” promotion ” >
</div>
<script src= ” @Url.Content( “ ~/Scripts/MoviesScripts.js ” ) ” type= ” text/javascript ” >
</script>
<h3><em>Fresh</em> off the grill</h3>
脚本配置节点
另一种嵌入脚本的方式,可以使用 Razor 视图中的默认脚本节点将脚本输出到视图中。在布局视图中,你可以选择渲染一个名为 scripts 的节点:
<head>
<title>@ViewBag.Title</title>
<link href= ” @Url.Content( “ ~/Content/Site.css ” ) ” rel= ” stylesheet ”
type= ” text/css ” />
<script src= ” @Url.Content( “ ~/Scripts/jquery-1.4.4.min.js ” ) ”
type= ” text/javascript ” ></script>
@RenderSection( “ scripts ” , required:false);
</head>
在任何内容视图中,你可以使用脚本配置节点在视图的头部注入脚本:
@section scripts{
<script src= ” @Url.Content( “ ~/Scripts/MusicScripts.js ” ) ”
type= ” text/javascript ” ></script>
}
使用节点配置可以精确的控制所需的脚本以及要包含的顺序。
其他部分脚本
在脚本文件夹中还有其它的 .js 脚本是做什么的?
为了 jQuery 的核心库文件以外,脚本文件夹中还包含两个 jQuery 插件—— jQuery UI 和 jQuery 验证。这些都是为 jQuery 核心库额外添加的功能,你将会在本章中使用到这两个插件。请注意这两个插件也有压缩版本的文件。
你还会找到名称中包含“ vsdoc ”的文件。这些文件能为 Visual Studio 提供更好的自动感应,你从来都不需要直接引用这些文件或将它们发送到客户端。当您在自定义的脚本文件中引用到这些文件, Visual Studio 会自动找到这些文件。
“不唐突”这个单词是在不唐突脚本与 jQuery 在 MVC 框架中整合是由微软提出的。在 ASP.NET MVC 框架中使用到 AJAX 功能你会用到这些文件,你会在本章看到如果使用这些脚本文件。
在文件夹中有很多之前由微软建立的微软 AJAX 库(例如 MicrosoftAjax.js )。由于 ASP.NET MVC 3 应用程序只依赖于 jQuery ,所以你可以安全的从应用程序中删除这些文件,它们支持为了向后兼容而已。
现在已经知道了 jQuery 是什么,以及如何在应用程序中引用脚本,接下来,看看在 MVC 框架中如果使用 AJAX 功能。
AJAX Helper
现在我们来看看 ASP.NET MVC 中的 HTML Helper 。你可以使用 HTML Helper 创建表单或者指向控制器动作的链接。同样在 ASP.NET MVC 中也存在一个 AJAX Helper 。 AJAX Helper 也可以创建表单和指向控制器动作的链接,只不过它们都是异步操作的。当你使用这些 Helper 时,你不需要编写任何脚本代码或做任何异步操作。 AJAX 的背后,是 jQuery 在 MVC 中不唐突的扩展。使用 Helper 你需要使用到 jquery.unobtrusive-ajax 脚本。如果你需要在应用程序中使用到此功能,那么你就可以在布局视图文件中引用这个文件(包括 jQuery )。
<script src= ” @Url.Content( “ ~/Scripts/jquery-1.4.4.min.js ” ) ”
type= ” text/javascript ” ></script>
<script src= ” @Url.Content( “ ~/Scripts/Scripts/jquery.unobtrusive-ajax.min.js ” ) ”
type= ” text/javascript ” ></script>
@RenderSection( “ scripts ” , required:false);
AJAX ActionLinks
在 Razor 视图中你可以像 HTML Helper 一样使用 AJAX Helper 使用各种扩展方法来操作 AJAX 。 AJAX 的 ActionLink 方法可以创建一个异步链接标记。试想一下,你要在 MVC 的音乐商店的页面上添加一个“每日交易”的链接。问用户们点击链接时,你不希望他们跳转浏览一个新页面,而是在现有页面上显示专辑的详细细节。
如果想做到这点,你可以在 Views/Home/Index.cshtml 视图中已存在的专辑列表底部添加下面的代码:
<div id= ” dailydeal ” >
@Ajax.ActionLink( “ Click here to see today ’ s special! ” ,
“ DailyDeal ” ,
new AjaxOptions{
UpdateTargetId= ” dailydeal ” ,
InsertionMode=InsertionMode.Replace,
HttpMethod= ” GET ”
})
</div>
ActionLink 的方法第一个参数指定是指定链接的文本,第二个参数是你要调用的动作的名称。像 HTML Helper 中的同名方法一样, AJAX ActionLink 中有不同的重载,你可以传入控制器名称、路由以及 HTML 属性值。但是也有一个不同的 AjaxOptions 参数。这个备选参数是知道如果发送请求,以及服务器会如何返回结果。选项中 也存在错误处理,显示加载的元素,以及显示确认对话框等。在这种情况下,你可以指定使用服务器返回结果替换 ID 为“ dailydeal ”的元素的内容,接下来你需要在 HomeController 控制器中添加 DailyDeal 动作来响应请求:
public ActionResult DailyDeal()
{
var album = GetDailyDeal();
return PartialView( “ _DailyDeal ” , album);
}
private Album GetDailyDeal()
{
return storeDB.Albums
.OrderBy(a => a.Price)
.First();
}
点击 AJAX 链接会从目标链接中返回响应的文本或 HTML 片段。在这个例子中,将会为你返回 HTML 分部视图。请将下面的代码添加到项目的 Views/Home 文件夹的 _DailyDeal.cshtml 文件中:
@model MvcMusicStore.Models.Album
<p>
<img alt= ” @Model.Title ” src= ” @Model.AlbumArtUrl ” />
</p>
<div id= ” album-details ” >
<p>
<em>Artist:</em>
@Model.Artist.Name
</p>
<p>
<em>Price:</em>
@String.Format( ” {0:F} ” , Model.Price)
</p>
<p class= ” button ” >
@Html.ActionLink( ” Add to cart ” , ” AddToCart ” ,
” ShoppingCart ” , new { id = Model.AlbumId }, ”” )
</p>
</div>
现在当用户点击链接时,一个异步请求就会被发送到 HomeController 的 DailyDeal 动作上。一旦触发操作,动作就会返回一个 HTML 片段,脚本在后台运行中会获取到这个 HTML 片段并替换掉 DOM 中 dailydeal 元素的内容。在用户点击之前,网站首页的底部如图 8-2 所示 。
图 8-2
在用户点击之后,这个页面(该页面并没有刷新)就变成了图 8-3 。
图 8-3
Ajax.ActionLink 生成的链接是如果从服务器上获取内容并在响应完成之后进行替换的呢?在下一节,我们会深入了解异步链接是如果工作的。
提示: 如果你想看到动作中的代码,可以使用 NuGet 来安装 Wrox.ProMvc3.Ajax.ActionLink 包。包中包含了 MVC 音乐商店的数据访问类,所以最好将包加入到工程中来,一旦你安装好了包,你就能在 /ActionLink 中看到新的首页。
HTML5 属性
加入你查看渲染完成的链接的源代码,你将会看到如下内容:
<a data-ajax= ” true ” data-ajax-method= ” GET ” data-ajax-mode= ” replace ”
data-ajax-update= ” #dailydeal ” href= ” /Home/DailyDeal ” >
Click here to see today's special!
</a>
不唐突的 JavaScript 的特点就是不会在 HTML 中看到任何 JavaScript 代码,而且肯定不会有任何脚本代码。如果你仔细观察,你会在指向动作的链接上看到 HTML 元素有经过编码并带有 data- 前缀的属性。
HTML5 规范允许使用 data- 属性为私有应用程序保持状态。换句话说, Web 浏览器是不会尝试去解释这些属性的内容,所以你可以使用这些属性随意存储自己的数据而不会影响页面的显示或渲染。 Data- 属性甚至可以在 HTML5 规范发布前的浏览器中也可以正常工作。例如, Internet Explorer 6 会直接忽略掉它不能解析的属性,所以 data- 属性在旧版本的 IE 浏览器中是完全安全。
在应用程序中添加 jquery.unobtrusive-ajax 文件的目的就是寻找 data- 属性,然后操纵元素的行为。如你所知 jQuery 可以很容易的找到元素,所以你可以想象下不唐突 JavaScript 文件中使用如下代码查找元素:
$(function () {
$( “ a[data-ajax]=true ” ). // do something
});
这段代码表示 jQuery 要查找所有 data-ajax 属性为 true 的链接, data-ajax 属性表示当前这个元素需要异步行为。一旦将于确定这个元素为异步元素,不唐突脚本就会去读取元素的其他设置项(如替换模式、更新目标或 HTTP 方法)和修改元素相应的行为(通常使用 jQuery 事件进行回调,也使用 jQuery 来发送请求)。所有的 ASP.NET MVC AJAX 功能都要使用到 data- 属性。
在下一节中,我们来看看:异步表单。
AJAX 表单
让我们来想象另外一个关于音乐商店首页的应用场景,你想为用户提供一个搜索艺术家的功能。因为需要用户输入内容,所有必须在页面上放置一个表单标记,但是它并不仅仅是个表单而是——异步表单。
@using (Ajax.BeginForm( “ ArtistSearch ” , “ Home ” ,
new AjaxOptions {
InsertionMode=InsertionMode.Replace,
HttpMethod= ” GET ” ,
OnFailure= ” searchFailed ” ,
LoadingElementId= ” ajax-loader ” ,
UpdateTargetId= ” searchresults ” ,
}))
{
<input type= ” text ” name= ” q ” />
<input type= ” submit ” value= ” search ” />
<img id= ” ajax-loader ”
src= ” @Url.Content( “ ~/Content/Images/ajax-loader.gif ” ) ”
style= ” display:none ” />
}
表单呈现之后,当用户点击提交按钮,浏览器就会对 HomeController 的 ArtistSearch 动作发起一个异步 GET 请求。请注意 AjaxOptions 中的 LoadingElementId 参数,当进入异步请求过程时,客户端框架就会自动显示该元素。通常,你需要在元素中放入一个动画,让用户知道后台正在有工作进行。此外,请注意这里还有一个 OnFailure 选项,以及与该选项相似的一些参数,你可以通过设置来捕获各种客户端的 AJAX 请求事件( OnBegin 、 OnComplete 、 OnSuccess 和 OnFailure )。你可以为这些参数设置 JavaScript 函数名称,以便在事件发生时调用。在此为 OnFailure 事件指定了一个名为“ searchFailed ”的函数,在运行时如果遇到搜索失败就为执行该函数(你可以将此函数置于 MusicScripts.js ):
function searchFailed() {
$( “ #searchresults ” ).html( “ Sorry, there was a problem with the search. ” );
}
你需要考虑如何去处理 onFailure 事件,如果用户在点击搜索按钮,而后台服务器代码返回了一次错误,而页面未反馈任何内容,用户会感觉很困惑。至少显示出错误的内容,以便告诉他们你已经操作了。
BeginForm Helper 的行为看起来有点像 ActionLink Helper 。最后,当用户点击提交按钮提交表单时,将会发起一个 AJAX 请求到服务器,服务器可以回应任意格式的内容。在客户端接收到响应时,不唐突脚本将会把响应内容放置到 DOM 中。在这个例子中,你将会替换掉 ID 为 searchresults 的元素。
在这个例子中,控制器的动作需要查询数据库中的内容并展示到分部视图中,你可以返回纯文本内容,但是你需要将艺术家的作为一个列表由动作呈现在分部视图中:
public ActionResult ArtistSearch(string q)
{
var artists = GetArtists(q);
return PartialView(artists);
}
private List<Artist> GetArtists(string searchString)
{
return storeDB.Artists
.Where(a => a.Name.Contains(searchString))
.ToList();
}
我们需要为分部视图获取模型并建立列表:在项目的 Views/Home 文件夹中创建名为 ArtistSearch.cshtml 的视图:
@model IEnumerable<MvcMusicStore.Models.Artist>
<div id= ” searchresults ” >
<ul>
@foreach (var item in Model) {
<li>@item.Name</li>
}
</ul>
</div>
提示: 你可以在自己的 MVC 音乐商店项目中添加代码,通过 NuGET 来安装 Wrox.ProMvc3.Ajax.AjaxForm 包并打开“ /AjaxForm ”就可以看到这个新首页了。
在本章的后面部分,我恶魔你将会回到搜索表单为它添加一些额外的功能。现在我们需要集中精力来看另外一项 ASP.NET MVC 框架内置的 AJAX 功能——客户端验证。
客户端验证
MVC 框架默认就支持数据注释属性的客户端验证。就像这个例子,你可以看到 Album 类的 Title 和 Price 属性:
[Required(ErrorMessage = “ An Album Title is required ” )]
[StringLength(160)]
public string Title { get; set; }
[Required(ErrorMessage = “ Price is required ” )]
[Range(0.01, 100.00,
ErrorMessage = “ Price must be between 0.01 and 100.00 ” )]
public decimal Price { get; set; }
这些属性需要数据注释来标注那些是必填项或定义这个属性的长度或值范围。 ASP.NET MVC 会通过模型粘合剂来触发属性的客户端验证,而客户端验证依赖于 jQuery 验证插件。
jQuery 验证
如前所述, jQuery 的验证插件( jquery.validate )默认存放在 MVC3 应用程序的 Scripts 文件夹中。如果你希望启用客户端验证功能就需要在将其加入到脚本标记中。在 StoreManager 文件夹的创建或笔记视图中,你将会看到如下代码:
<script src= ” @Url.Content( “ ~/Scripts/jquery.validate.min.js ” ) ”
type= ” text/javascript ” ></script>
<script src= ” @Url.Content( “ ~/Scripts/jquery.validate.unobtrusive.min.js ” ) ”
type= ” text/javascript ” ></script>
第一个标记是加载压缩过的 jQuery 验证插件, jQuery 的验证插件已经与所有事件挂钩(如提交或获得焦点等事件),并在事件触发时执行客户端验证规则,插件还提供了一整套默认的验证规则。
Web.config 中的 AJAX 设置
默认情况下,不唐突 JavaScript 和客户端验证在 ASP.NET MVC 应用程序中默认是启用的。尽管如此,你还是可以通过 Web.config 中的 appSettings 来配置其行为。如果你打开应用程序根目录中的 Web.config 文件,将会看到 appSettings 配置项:
<appSettings>
<add key= ” ClientValidationEnabled ” value= ” true ” />
<add key= ” UnobtrusiveJavaScriptEnabled ” value= ” true ” />
</appSettings>
如果你想将整个应用程序的功能关闭,你可以将设置更改为 false 。此外,你也可以他哦在视图的基础上进行设置。在某个视图中,使用 HTML Helper 来设置 EnableClientValidation 和 EnableUnobtrusiveJavaScript 选项来覆盖配置项中的设置。
禁用功能选项主要是为了兼容与其相依赖的 Microsoft AJAX 库,而不是 jQuery 库或自定义脚本。
第二个脚本标签包含了 jQuery 验证的微软不唐突适配器。这个脚本文件的源代码是 MVC 框架创建用来为 jQuery 验证适配(转换)验证元数据的(这样 jQuery 就能明白该做什么了)。元数据是从哪来的?首先,你还记得是如何为专辑创建编辑视图的吗?在 Shared 文件夹的专辑编辑模板中你需要为视图嵌入 EditorForModel 类。模板代码如下:
<p>
@Html.LabelFor(model => model.Title)
@Html.TextBoxFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</p>
<p>
@Html.LabelFor(model => model.Price)
@Html.TextBoxFor(model => model.Price)
@Html.ValidationMessageFor(model => model.Price)
</p>
在这里 TextBoxFor 是关键, Helper 会为模型构建基于元数据的输入。当 TextBoxFor 看到验证元数据,如价格和标题的 Required 和 StringLength 注释,它们会被呈现在 HTML 中。下面是生成 Title 属性的标记:
<input
data-val= ” true ”
data-val-length= ” The field Title must be a string with a maximum length of 160. ”
data-val-length-max= ” 160 ” data-val-required= ” An Album Title is required ”
id= ” Title ” name= ” Title ” type= ” text ” value= ” Greatest Hits ” />
在这里你又一次看到了 data- 属性, jquery.validate.unobtrusive 脚本会利用元数据找到需要验证的元素(从 data-val=true 开始),并将 jQuery 验证插件与元素按照接口连接然后根据元数据验证规则执行验证。 jQuery 验证会在每次案件或焦点事件时被触发,并在错误时及时给用户发起反馈。验证插件也会在提交表单时验证错误,这也意味着你不需要在服务器请求时单独处理错误。
要更进一步了解工作中的细节,就看下一节,自定义客户端验证方案。
自定义验证
在第六章 , 你使用下面的代码 , 利用 MaxWordsAttribute 属性来验证一个字符串中所有的单词数量:
public class MaxWordsAttribute : ValidationAttribute
{
public MaxWordsAttribute(int maxWords)
:base( “ Too many words in {0} ” )
{
MaxWords = maxWords;
}
public int MaxWords { get; set; }
protected override ValidationResult IsValid(
object value,
ValidationContext validationContext)
{
if (value != null)
{
var wordCount = value.ToString().Split( ‘ ‘ ).Length;
if (wordCount > MaxWords)
{
return new ValidationResult(
FormatErrorMessage(validationContext.DisplayName)
);
}
}
return ValidationResult.Success;
}
}
你可以使用下面这段代码的属性,但是该属性只提供了服务器端的验证支持:
[Required(ErrorMessage = “ An Album Title is required ” )]
[StringLength(160)]
[MaxWords(10)]
public string Title { get; set; }
为了支持客户端验证,在下章我们会讨论为你的属性实现一个接口。
IClientValidatable
IClientValidatable 接口定义了一个方法: GetClientValidationRules 。 MVC 会在验证这个对象时发现这个接口,他会调用 GetClientValidationRules 来检索到一个 ModelClientValidationRule 对象的数组。这些对象会将元数据、规则通过框架发送到客户端。
你可以用下面的代码来实现自定义验证器的接口:
public class MaxWordsAttribute : ValidationAttribute,
IClientValidatable
{
…
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
rule.ValidationParameters.Add( “ wordcount ” , WordCount);
rule.ValidationType = “ maxwords ” ;
yield return rule;
}
}
你需要确认有多少信息需要在客户端进行验证:
如果验证失败,需要显示什么错误信息; 允许使用多少个字; 还有一段 JavaScript 代码的标识;请注意,如果你需要在客户端触发多种类型的验证,你可以同时返回多个规则。
代码会从规则的 ErrorMessage 属性中取出错误信息。这样可让服务器的错误信息与客户端的错误信息完全匹配。 ValidationParameters 集合可以容纳你所需要的客户端参数,例如允许的最大字符数等。你可以把额外的参数放置到这个集合中,不但要注意它们的名称,必须符合客户端脚本中的名称。最后,在 ValidationType 属性会标识你需要在客户端上使用 JavaScript 代码。
MVC 框架需要从 GetClientValidationRules 方法来获得规则,并序列化后设置到客户端 data- 属性中:
<input
data-val= ” true ”
data-val-length= ” The field Title must be a string with a maximum length of 160. ”
data-val-length-max= ” 160 ”
data-val-maxwords= ” Too many words in Title ”
data-val-maxwords-wordcount= ” 10 ”
data-val-required= ” An Album Title is required ” id= ” Title ” name= ” Title ”
type= ” text ” value= ” For Those About To Rock We Salute You ” />
请注意, maxwords 是如何将属性名对应到 MaxWordsAttribute 属性上的,是因为你在 ValidationType 属性中设置了 maxwords 的文本(必须注意,验证的类型和所有验证参数名必须小写,因为命名必须符合 HTML 属性规则)。
现在,你已经具有了客户端上的元数据,但是你仍然要编写一些脚本代码来执行验证逻辑。
自定义验证脚本代码
幸运的是 , 你无需为客户端 data- 属性中的元数据而编写任何代码,但是,依然需要在验证中填写两端代码:
适配器:适配器是为了匹配 MVC 框架的不唐突扩展部分所需要的元数据,不唐突扩展需要将 data- 属性中的元数据通过适配转换成为 jQuery 验证可以理解的值; 本身的验证规则:这在 jQuery 中被称为验证器。这些代码都存储同一个脚本文件中,这时假设你想将代码存入由“自定义脚本”一节所创建的 MusicScripts.js 文件中。在这种情况下,你需要确保 MusicScript.js 文件会在验证脚本载入之后再进行加载。如下使用脚本配置元素进行创建:
@section scripts
{
<script src= ” @Url.Content( “ ~/Scripts/jquery.validate.min.js ” ) ”
type= ” text/javascript ” ></script>
<script
src= ” @Url.Content( “ ~/Scripts/jquery.validate.unobtrusive.min.js ” ) ”
type= ” text/javascript ” ></script>
<script src= ” @Url.Content( “ ~/Scripts/MusicScripts.js ” ) ” type= ”
text/javascript ” >
</script>
}
将这些引用代码加入到 MusicScripts.js 中,可以为你提供你需要的代码提示功能:
/// <reference path= ” jquery-1.4.4.js ” />
/// <reference path= ” jquery.validate.js ” />
/// <reference path= ” jquery.validate.unobtrusive.js ” />
第一部分代码是实现适配器的, MVC 框架的不唐突验证扩展会在 jQuery.validator.unobtrusive.adapters 对象中存储所有适配器。你可以使用适配器对象公开的 API 来增加新的适配器,如表 8-2 所示:
表 8-2 : 适配器方法
名称
描述
addBool
为验证器的规则创建一个“开”或“关”适配器。这个方法没有额外的参数。
addSingleVal
为验证器的规则创建一个从元数据匹配单个参数的适配器。
addMinMax
创建一个适配器映射到一组验证规则上,做一个最小值或一个最大值对比检查。或者同时对最大值、最小值一起进行数据规则检查。
add
创建一个与前面提供类型不能匹配的适配器,它需要额外的参数或额外的设置代码。
在验证最大值的情况下,你可以使用 addSingleVal 或 AddMinMax 方法(或 add ,因为它可以实现任何情况)。如果你不需要检查最小值的话,就可以使用 addSingleVal 的 API ,如下代码:
/// <reference path= ” jquery-1.4.4.js ” />
/// <reference path= ” jquery.validate.js ” />
/// <reference path= ” jquery.validate.unobtrusive.js ” />
$.validator.unobtrusive.adapters.addSingleVal( “ maxwords ” , “ wordcount ” );
第一个参数是适配器的名称,必须与服务器端设置规则的 ValidationProperty 的值相同。第二个参数是用来检索元数据的参数名称。请注意,不需要在参数名中使用 data- 前缀;这里的参数需要与你在服务器端设置的 ValidationParameters 集合中的参数名相匹配。
适配器相对比较简单,它主要目的就是为不唐突扩展定位并识别元数据。有了合适的适配器,就可以写验证。
所有验证器都在 jQuery.validator 对象中,与适配器对象一样,新验证器也是通过验证对象的 API 来添加的。该方法名是“ addMethod ”:
$.validator.addMethod( “ maxwords ” , function (value, element, maxwords) {
if (value) {
if (value.split( ‘ ‘ ).length > maxwords) {
return false;
}
}
return true;
});
这个方法需要提供两个参数:
验证器名称,按照惯例应与适配器名称相匹配(和服务器上的 ValidationType 属性相匹配); 验证发生时所需调用的函数。验证函数会接受三个参数,可以返回真(验证通过)或假(验证失败):
方法的第一个参数将包含输入的值(例如专辑的标题); 方法的第二个参数是输入元素,它包含验证所需的值(在这个例子中并没有提供相应的信息); 方法的第三个参数是一个包含所有验证参数的数据,在这个例子中,只包含了一个验证参数(字符串的最大长度)。虽然 ASP.NET MVC 中的 AJAX Helper 提供了大量功能,但整个生态系统需要更多的 jQuery 扩展。在下一节中专门讨论这个主题。
独立助手
如果在浏览器中访问 http://plugin.jquery.com ,你会看到有成千上万的 jQuery 扩展。这些扩展大多是面向图形方面的也有很多相关方面的事情(动画方面)。或者其它类似于日期选择器或 Gird 的插件。
使用 jQuery 插件通常需要下载、解压插件并将这些插件添加到你的项目中。一些 jQuery 插件可以通过 NuGet 获得包并轻松加入到你的项目中。插件至少需要一个 Javascript 文件,许多面向 UI 的插件可能还需要附带图像以及样式表文件。
每个 ASP.NET MVC 项目生成时都会有两个插件: jQuery 验证(你之前使用过的)和 jQuery UI (你现在要看到的)。
jQuery UI
jQuery UI 是 jQuery 的一个插件,包含效果和 Widgets 。像所有插件一样,它与 jQuery 紧密集成并扩展了 jQuery 的 API 。下面回到本章开头的那个例子中——专辑商店的专辑封面动画代码:
$(function () {
$( “ #album-list img ” ).mouseover(function () {
$(this).animate({ height: ‘ +=25 ’ , ‘ +=25 ’ })
.animate({ height: ‘ -=25 ’ , ‘ -=25 ’ });
});
});
使用 jQuery UI 来代替专辑封面反转的动画 . 第一步 , 你需要在应用程序的布局视图红添加 jQuery UI 的脚本引用标签:
<script src= ” @Url.Content( “ ~/Scripts/jquery-1.4.4.min.js ” ) ”
type= ” text/javascript ” ></script>
<script src= ” @Url.Content( “ ~/Scripts/jquery.unobtrusive-ajax.min.js ” ) ”
type= ” text/javascript ” ></script>
<script src= ” @Url.Content( “ ~/Scripts/jquery-ui.min.js ” ) ”
type= ” text/javascript ” ></script>
现在,你可以将 mouseover 事件处理程序改为下面的代码:
$(function () {
$( “ #album-list img ” ).mouseover(function () {
$(this).effect( “ bounce ” );
});
});
当用户使用鼠标滑过专辑时,封面图片会在很短的时间内反转。正如你看到的,你可以使用 jQuery 返回的包装集来执行 UI 扩展插件的方法,这个方法还提供了第二个“可选”参数,它允许你通过参数来调整行为。
$(this).effect( “ bounce ” , { time: 3, distance: 40 });
你可以通过阅读 jQuery.com 插件频道的文档来了解这些可选值(以及它们的默认值)。在 jQuery UI 中还包含 explode , fade , shake 和 pulsate 等效果。
可选项!可选项!无处不在!
“可选项”参数会始终贯穿 jQuery 和 jQuery 插件。某些方法可能会需要 6 、 7 个不同的参数(如时间、距离、方向或模式等),你可以传递一个对象,通过对象的属性来设置参数。在前一个例子中,需要设置的只有时间和距离。该文档会始终告诉你有哪些可用的参数,并且每个参数的默认值是什么。你构造的对象只需要属性和要设置的参数相对应就好了!
jQuery UI 能做的不光是特效或吸引眼球。该插件还包含像 Accordion 、 autocomplete ,按钮,日期选择器,对话框,进度条,滑动条或 tabs 。下一节我们会做一个自动完成的例子。
jQuery UI 自动完成
作为一个 widget , AutoComplete 需要可以定位在屏幕的新用户界面元素上。这个元素需要在颜色、字体大小、背景和一些典型细节方面与用户界面的其他元素保持一致。 jQuery UI 依赖于主题来提供展示细节。 jQuery UI 的主题包含样式表和图片文件。每个新创建的 MVC 项目的 Content 文件夹中都会有一个 base 的主题,这个主题会包含样式表( jQuery-ui.css )和 images 文件夹中的 .png 文件。
在使用 AutoComplete 之前,你需要设置应用程序的布局视图包含 base 主题的样式表信息:
<link href= ” @Url.Content( “ ~/Content/Site.css ” ) ” rel= ” stylesheet ”
type= ” text/css ” />
<link href= ” @Url.Content( “ ~/Content/themes/base/jquery-ui.css ” ) ”
rel= ” stylesheet ” ) ” type= ” text/css ” />
<script src= ” @Url.Content( “ ~/Scripts/jquery-1.4.4.min.js ” ) ”
type= ” text/javascript ” ></script>
<script src= ” @Url.Content( “ ~/Scripts/jquery.unobtrusive-ajax.min.js ” ) ”
type= ” text/javascript ” ></script>
<script src= ” @Url.Content( “ ~/Scripts/jquery-ui.min.js ” ) ”
type= ” text/javascript ” ></script>
如果你不喜欢 jQuery 内置的默认主题,你可以通过访问 http://jqueryui.com/themeroller/ 去下载主题。你还可以通过网页定制(可实时预览效果)和下载自定义的 jQuery-ui.css 文件。
添加行为
首先,你是否还记得在本章前面部分“艺术家搜索”功能中使用“ Ajax 表单”的情景?你需要使用 JavaScript 找到输入节点并绑定 jQuery 自动完成的行为。使用这个功能的方法之一就是用 MVC 框架的 data- 属性:
<input type= ” text ” name= ” q ”
data-autocomplete-source= ” @Url.Action( “ QuickSearch ” , “ Home ” ) ” />
通过使用 jQuery 查找节点的 data-autocomplete-source 属性,这将会告诉你这个元素要一个 AutoComplete 的行为,并未 AutoComplete 部件制定一个数据源,可以让它用这个来检索候选人。 AutoComplete 通过指定 URL 获取远程数据源(获取一个对象数组)并缓存在内存中会比较容易消耗内存。控制每次从远程数据源获取艺术家的数量,发送合理的数据量到客户端。
在 MusicScripts.js 中,你可以使用下面的代码,在 ready 事件中为所有 AutoComplete 的 data-autocomplete-source 属性的输入框附加 AutoComplete 方法:
$( “ input[data-autocomplete-source] ” ).each(function () {
var target = $(this);
target.autocomplete({ source: target.attr( “ data-autocomplete-source ” ) });
});
jQuery 的 each 函数会遍历所有项,在方法中,你可以为目标调用自动完成插件的方法。 AutoComplete 方法的参数只有一个可选参数,与大多数方法不同它还有一个必填参数——数据源属性。你可以设置其他选项,例如在按键跳起的延迟之后开始合计金额,也可以在自动完成之后发送到数据源获取所需的最小字符数。
在这个例子中,你需要指出一个控制器动作,下面是代码(只是 为了防止你忘记):
<input type= ” text ” name= ” q ”
data-autocomplete-source= ” @Url.Action( “ QuickSearch ” , “ Home ” ) ” />
AutoComplete 将会按照预期访问数据源并使用接收到的对象集合来建立一个用户列表。在 HomeController 中的 QuickSearch 动作中要返回一个 autocomplete 可以解析的数据。
绑定数据源
AutoComplete 希望通过调用数据源并获取到 JSON 格式的对象。幸运的是,你在稍后会看到使用 MVC 控制器的动作容易就能生成一个 JSON 结果。对象必须有 Label 的属性或 Value 的属性,或两者都有。 Autocomplete 会显示用户在使用的 label 属性。当用户选择了自动完成列表中的项目,该部件将会把所选的内容放入输入框中。如果你不提供 Label 或 Value , Autocomplete 将会使用任意属性替代 Label 或 value 。
使用下面的代码实现 QuickSearch ,并返回 JSON 结果:
public ActionResult QuickSearch(string term)
{
var artists = GetArtists(term).Select(a => new {value = a.Name});
return Json(artists, JsonRequestBehavior.AllowGet);
}
private List<Artist> GetArtists(string searchString)
{
return storeDB.Artists
.Where(a => a.Name.Contains(searchString))
.ToList();
}
当 Autocomplete 调用数据源时,它会将输入项中的值作为查询字符串,你会在动作的参数中接收到这个值。你会在一个匿名类型对象中将美味艺术家转化成 JSON 集合并产生一个 JsonResult 。当框架执行结束会将这个结果序列化成 JSON 对象。
JSON 劫持
默认情况下, ASP.NET MVC 框架是不允许在 HTTP 的 GET 请求时响应并加载 JSON 结果。你需要发送一个 JSON 的 GET 请求,你需要将使用 JsonRequestBehavior.AllowGet 作为第二个参数明确表明允许获取 JSON 方法。
然后,即使这样,恶意用户还是可以在访问 JSON 时进行 JSON 劫持。如果你不想在 GET 请求的 JSON 结果中返回敏感信息,你可以参看 Phil 的文章:
http://haacked.com/archive/2009/06/25/json-hijacking.aspx 。
图 8-4 中显示出了您的劳动成果。
图 8-4
JSON 不仅非常容易访问的控制器的动作,而且它非常轻量级。事实上, JSON 请求和响应所产生的负载比同样体积的 HTML 或 XML 数据要小很多。一个很好的例子就是搜索功能。当用户点击搜索按钮时,你最终会呈现艺术家列表的局部视图。你可以使用 JSON 结果来代替原有视图来减少带宽用量。
从服务器检索 JSON 数据的核心问题是,是如何序列化对象,可以让它好呢容易的从服务器传送到页面的 HTML 。你需要使用原始数据在客户端构建 HTML 。模板是这些繁琐的传统工作更为容易。
JSON 和 jQuery 模板
jQuery 模板是 jQuery 的插件,默认情况下并不包含在 MVC3 的项目中,你可以从 NuGet 中获得此插件。模板可以帮助你在客户端构建 HTML 。这个语法类似于 Razor 视图,从某种意义上说,你使用特殊的 HTML 分隔符来标识数据出现的位置,这种方法被称为占位符绑定表达式。参看下面的代码示例:
<span class=”detail”>
Rating: ${AverageReview}
Total Reviews: ${TotalReviews}
</span>
上面的模板对应的 AverageReview 和 TotalReviews 属性对象。当渲染 jQuery 模板时,模板会将值放置在相应的位置。您也可以针对模板进行数据序列进行渲染。你可以通过以下地址来访问 jQuery 模板的文档:
http://api.jquery.com/category/plugins/ t emplates/ 。
在下面的章节中,你将会使用 JSON 和 jQuery 模板来重写搜索功能。
jQuery 模板的来源
jQuery 模板是由微软创作的一个 jQuery 官方插件的开源项目。事实上,微软正在实施几个 jQuery 系统的插件,包含 jQuery 模板、 jQuery 数据链接和 jQuery 全球化插件。
添加模板
安装 jQuery 模板,请右键单击 MvcMusicStore 项目,并选择“添加到库包引用”。在对话框(如图 8-5 所示)中搜索“ jQuery Templates ”:
图 8-5
当 NuGet 包添加到项目中,项目的 Scripts 文件夹中会多了两个新的脚本: jQuery.tmpl.js 和 jQuery.tmpl.min.js 。在实际使用中要将插件的压缩版本发送到客户端。
<script src=”@Url.Content(“~/Scripts/jquery-1.4.4.min.js”)”
type=”text/javascript”></script>
<script src=”@Url.Content(“~/Scripts/jquery.unobtrusive-ajax.min.js”)”
type=”text/javascript”></script>
<script src=”@Url.Content(“~/Scripts/jquery-ui.min.js”)”
type=”text/javascript”></script>
<script src=”@Url.Content(“~/Scripts/jquery.tmpl.min.js”)”
type=”text/javascript”></script>
插件放置完毕,就可以开始使用模板来实现搜索功能。
修改搜索表单
在本章的前面“ Ajax 表单”一节,使用 AjaxHelper 创建了艺术家搜索功能:
@using (Ajax.BeginForm(“ArtistSearch”, “Home”,
new AjaxOptions {
InsertionMode=InsertionMode.Replace,
HttpMethod=”GET”,
OnFailure=”searchFailed”,
LoadingElementId=”ajax-loader”,
UpdateTargetId=”searchresults”,
}))
{
<input type=”text” name=”q”
data-autocomplete-source=”@Url.Action(“QuickSearch”, “Home”)” />
<input type=”submit” value=”search” />
<img id=”ajax-loader”
src=”@Url.Content(“~/Content/Images/ajax-loader.gif”)”
style=”display:none”/>
}
虽然 AjaxHelper 提供了很多功能,你要删除这个助手,并从头开始。 jQuery 提供各种从服务器检索数据的异步 API 。你可以利用这些功能间接来使用自动 autocomplete widget 等控件。
你首要改变使用 jQuery Ajax Helper 的搜索方式,但是控制器代码还是保持不变的(现在还没有使用 JSON )。在 Index.cshtml 中的的新标记代码如下:
<form id=”artistSearch” method=”get” action=”@Url.Action(“ArtistSearch”, “Home”)”>
<input type=”text” name=”q”
data-autocomplete-source=”@Url.Action(“QuickSearch”, “Home”)” />
<input type=”submit” value=”search” />
<img id=”ajax-loader” src=”@Url.Content(“~/Content/Images/ajax-loader.gif”)”
style=”display:none”/>
</form>
这里代码唯一的变化就是没有使用 AjaxHelper 的 BeginForm 来构建表单。没有 Helper 你还需要编写自己的 Javascript 代码从服务器请求 HTML 。你可以在 MusicScripts.js 内写如下代码:
$(“#artistSearch”).submit(function (event) {
event.preventDefault();
var form = $(this);
$(“#searchresults”).load(form.attr(“action”), form.serialize());
});
使用代码钩子的形式来提交表单。
jQuery 需要调用事件传入的 preventDefault 方法来防止发生违约事件的行为(在这种情况下,你需要控制请求和响应,以避免直接提交到服务器)。
load 方法会从 URL 中检索 HTML 然后放置到匹配的 HTML 元素( searchresults 元素)。 load 方法的第一个参数表单的 action 属性值。第二个参数是要传送的查询字符串。 jQuery 的 serialize 方法会将表单内部的值都链接成一个字串数据。在这个例子中,你只有一个文本输入单的值,当用户输入 black 时,序列化会使用输入框的 name 和 value 属性,建立起查询字符串“ q=black ”。
获得 JSON
你已经修改了代码,但是你还需要重新调整由服务器返回的 HTML 代码。让我们来更改 HomeController 控制器中的 ArtistSearch 动作,并返回 Json ,而不是局部视图:
public ActionResult ArtistSearch(string q)
{
var artists = GetArtists(q);
return Json(artists, JsonRequestBehavior.AllowGet);
}
现在,你需要将客户端接收脚本改为 Json 而不是 HTML 片段。 jQuery 提供名为 getJSON 的方法,你可以使用它来检索数据:
$(“#artistSearch”).submit(function (event) {
event.preventDefault();
var form = $(this);
$.getJSON(form.attr(“action”), form.serialize(), function (data)
// now what?
});
});
相比较原来的代码,只是由原来的 load 方法改为调用 getJSON 方法,但是方法的参数并不相同。需要一个网址和一些查询字符串数据,这样会产生一个 HTTP 的 GET 请求方法,进而给第三个参数的回调方法传入一个反序列化后的 JSON 的对象,你需要在回调里面做些什么呢? JSON 数据里面是搜索的艺术家结果的数组,但是没有标记来呈现这些艺术家。这时模板就开始发挥作用。模板是在脚本标记内嵌入的标记。下面的代码显示模板是如果来呈现搜索结果的标记:
<script id=”artistTemplate” type=”text/x-jquery-tmpl”>
<li>${Name}</li>
</script>
<div id=”searchresults”>
<ul id=”artist-list”>
</ul>
</div>
注意脚本标记的 type 属性的值为“ text/x-jquery-tmpl ”。这样可以确保浏览器不会试图去把这些当作真正的脚本标记的内容去解释。 ${Name} 语法是绑定表达式。绑定表达式会告诉模板引擎找到当前数据对象的属性的名称,并将其填入 <li> 和 </li> 之间。这样就会将 JSON 数据呈现在标记间。
你可以在 getJSON 回调方法中选择使用你需要的模板:
$(“#artistSearch”).submit(function (event) {
event.preventDefault();
var form = $(this);
$.getJSON(form.attr(“action”), form.serialize(), function (data) {
$(“#artistTemplate”).tmpl(data).appendTo(“#artist-list”);
});
});
tmpl 方法能将 JSON 数据绑定到 DOM 元素中。因为 JSON 数据是一个 artists 的数组,模板引擎将会递归每个艺术家的数据,并根据代码模板输出到艺术家列表。
客户端模板是一个强大的技术,本节只是简单的了解了一下模板引擎的基本功能。这个示例只是在实现前面 AjaxHelper 的功能。你是否还记得在前面“ AjaxHelper ”一节中会在服务器抛出一个错误时会调用一个方法,会要求 Helper 类呈现出来一个 GIF 动画,你也可以通过删除一个抽象层次来实现这些功能。
jQuery.ajax 的最大灵活性
当你需要完全控制 Ajax 请求就需要使用 jQuery 的 Ajax 方法。 Ajax 方式有一些备选参数,你可以指定一个 HTTP 的行为动词( GET 或 POST )、超时、错误处理或其他。而所有你所见过的其他异步通信方法( load 或 getJSON )最终需要调用 Ajax 方法。
即使使用 Ajax 方法,你也依然可以使用 AjaxHelper 或客户端模板的所有功能:
$(“#artistSearch”).submit(function (event) {
event.preventDefault();
var form = $(this);
$.ajax({
url: form.attr(“action”),
data: form.serialize(),
beforeSend: function () {
$(“#ajax-loader”).show();
},
complete: function () {
$(“#ajax-loader”).hide();
},
error: searchFailed,
success: function (data) {
$(“#artistTemplate”).tmpl(data).appendTo(“#artist-list”);
}
});
});
调用 Ajax 方法比较复杂你需要定制很多的参数设置,就像为 load 或 getJSON 方法指定 url 和 data 属性一样, ajax 方法为你提供了发送和完成时的回调方法。 jQuery 将在完成或出错时调用这些回调方法。但是,错误和成功这两个回调方法在完成时只会有一个被执行。在这个例子中如果 jQuery 调用失败则会调用你在” Ajax 表单“那一节所定义的 searchFailed 方法,如果执行成功,你将会看到由模板设定的呈现内容。
改善 AJAX 性能
当你开始向客户端发送大量脚本代码时,你需要注意保持性能。有很多工具可以帮助你来优化网站的客户端性能,包括 Firebug 的 YSlow (详情见 http://developer.yahoo.com/yslow/ )和 Internet Explorer 的开发工具(详情见 http://msdn.microsoft.com/en-us/library/dd565629(VS.85).aspx )。在本章中,我们将会提供一些关于性能的优化技巧。
使用内容分发网络( CDN )
虽然你可以使用自己的服务器来分发 jQuery 脚本,而不考虑交给 jQuery 的内容交付网络( CDN )。 CDN 的缓存服务器分布在世界各地,使用它将会给客户带来更快的下载。因为其他网站也会从 CDN 引用 jQuery ,客户可能已经在本地拥有了文件缓存,这样做可能会为别人节省带宽成本。
微软就是这样一个 CDN 提供商。微软的 CDN 会承载本章中使用过的所有文件。如果你想从微软的 CDN 服务器来获取 jQuery 服务而不是自己的服务器,你可以使用以下的脚本标记:
<script src=”http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.4.4.min.js”
type=”text/javascript”></script>
你可以在下面的网址查找到的微软的 CDN 所提供的文件列表和最后发行版本:
http://www.asp.net/ajaxlibrary/CDN.ashx
脚本优化
许多 Web 开发人员并不会在文档的 head 元素中使用脚本标记。相反,他们尽可能的将脚本放置在靠近页面底部。因为脚本标记放置在页面顶部的 head 元素中,当浏览器遇到这个脚本标记时就会下载整个脚本,这种行为会导致页面加载缓慢。可以将所有脚本标记移动到页面底部(在 body 标记关闭前),将可以产生更好的用户体验。
有种技术,通过压缩自定义的脚本来减少页面加载的时间。正如本章前面“ Using jQuery ”中提到的,压缩 Javascript 可以让下载量减少一半。微软有一个非常棒的 Javascript 压缩器 http://ajaxmin.codeplex.com/ 。
最后,还有另外一种脚本优化技术,可以尽量减少你发送客户端的脚本内容。可与任何给定页面浏览器都回看到这些脚本标记。为了能使传输达到理想效果,你可以将多个 Javascript 文件合并成单个资源。多个脚本在合并时,会在项目中创建一个新的文件,在其他脚本运行时,会在发生 HTTP 请求时动态结合这些内容。你可以通过以下地址访问脚本动态合并项目 http://combres.codeplex.com/ 。
小结
本章走马观花式的了解了一下 ASP.NET MVC3 的 AJAX 功能。正如你所知道的,这些功能很大程度上依赖于开源的 jQuery 库,以及一些 jQuery 的常用插件。
在 ASP.NET MVC3 应用程序中 AJAX 功能成功的关键在于对 jQuery 内容和 jQuery 如何工作的了解。 jQuery 灵活而强大,它允许你从页面代码中将脚本代码分离,并支持不唐突 Javascript 。分离以为着你可以专注于编写更好的 Javascript 代码,并发掘 jQuery 的更多更好的功能。
分类: ASP.NET MVC
标签: asp.net mvc , asp.net mvc 3 , 翻译
http://www.cnblogs.com/o2ds/archive/2012/06/27/2565371.html
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于AJAX(Professional ASP.NET MVC 3的详细内容...