[翻译]《ASP.NET MVC 3 高级编程》第八章:AJAX(Professional ASP.NET MVC 3 --- Chapter 8: AJAX)

作者: O2DS
发布时间:2015-06-17 11:29:10

忙不是理由,好久没更新,懒惰了不少。感谢各位(@春華秋實@三桂)督促我更新排版结构,O2DS敬上。

索引
jQuery

AJAX助手

客户端验证

独立助手

改善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’, width: ‘+=25’ })        .animate({ height: ‘-=25’, width: ‘-=25’ });    });  });

代码的第一行调用了jQuery函数($),传递给匿名JavaScript函数一个参数。

$(function () {    $(“#album-list img”).mouseover(function () {      $(this).animate({ height: ‘+=25’, width: ‘+=25’ })        .animate({ height: ‘-=25’, width: ‘-=25’ });    });  });

jQuery会假定你提供了一个方法,并在浏览器从服务器端加载完成HTML(DOM)信息并建立完成文档对象模型时立即执行。因为在这个时候,你就可以安全而完善的执行DOM有关的脚本了。

第二行代码向jQuery函数传递了字符串参数“#album-list img”:

$(function () {    $(“#album-list img”).mouseover(function () {      $(this).animate({ height: ‘+=25’, width: ‘+=25’ })        .animate({ height: ‘-=25’, width: ‘-=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’, width: ‘+=25’ })        .animate({ height: ‘-=25’, width: ‘-=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’, width: ‘+=25’ })      .animate({ height: ‘-=25’, width: ‘-=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的美妙之处。

jQueryAJAX

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’, width: ‘+=25’ })        .animate({ height: ‘-=25’, width: ‘-=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&#39;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’, width: ‘+=25’ })      .animate({ height: ‘-=25’, width: ‘-=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表单”的情景

来源:http://www.cnblogs.com/o2ds/archive/2012/06/27/2565371.html

推荐: