ASP.NET MVC5高级编程 之 Ajax

时间:2023-03-09 16:59:29
ASP.NET MVC5高级编程 之 Ajax

jQuery不仅支持所有现代浏览器,包括IE、Firefox、Safari、Opera和Chrome等,还可以在编写代码和浏览器API冲突时隐藏不一致性(和错误)。

1. jQuery

jQuery擅长在HTML文档中查找、遍历和操纵HTML元素。一旦找到元素,jQuery就可以方便的在其上进行操作,如连接事件处理程序、使其具有动画效果以及围绕它的Ajax交互等。

1.1 jQuery函数

jQuery函数对象可以用来访问jQuery特性。

 $(function (){
     $("#album-list img").mouseover(function(){
         $(this).animate({ height: '+=25', width: '+=25'})
                .animate({ height: '-=25', width: '-=25'});
     });
 });
  • 第一行代码调用了jQuery函数($),并向其中传递了一个匿名的JavaScript函数作为第一个参数:
$(function (){

当传递一个函数作为第一个参数时,jQuery就会假定这个函数是要在浏览器完成构建(由服务器提供的)HTML页面中的文档对象模型(Document Object Model, DOM)后立即执行,换句话说,这个函数在从服务器端加载完HTML页面之后执行。这样就可以安全的执行函数中与DOM冲突的脚本,这种情况称为“DOM准备”事件。

  • 第二行代码向jQuery函数传递一个字符串“#album-list img”:
 $("#album-list img").mouseover(function(){

jQuery把这个字符串解释为选择器。选择器会告知jQuery需要在DOM中查找的元素。我们可以使用像类名和相对位置这样的特性值来查找元素。这一行代码的意图是告知jQuery查找id值为“album-list”的元素中的所有图像

当执行选择器时,它会返回一个包含零个或多个匹配元素的封装集(wrapped set)。我们可以调用其他任何jQuery方法来操作封装集中的元素。例如,上面的代码调用mouseover方法为与选择器匹配的每个图像元素的onmouseover事件连接处理程序。

  • jQuery利用JavaScript的函数式编程特性,经常把创建的或传递的函数作为jQuery方法的参数。为了表达事件触发时想进行的处理,就向mouseover方法传递了一个包含事件处理代码的函数参数:
 $(this).animate({ height: '+=25', width: '+=25'})
        .animate({ height: '-=25', width: '-=25'});

上面的例子实现了在触发mouseover事件时,匹配选择器的img元素会产生动画效果。在上面的代码中,之所以使用this关键字来引用要做动画效果的元素,是因为this是指向的是触发事件的元素

注意代码第一次将元素传递给jQuery函数的方法($(this))。jQuery将该参数看成一个元素的引用参数,并返回一个包含有该元素的封装集。

一旦将某个元素包含在jQuery封装集中,就可以调用jQuery方法(如animate)来操纵这个元素。示例中的代码首先将图像放大,然后在缩小的效果。

1.2 jQuery选择器

选择器是指传递给jQuery函数的,用来在DOM中选择元素的字符串。

例子 意义
$("#header") 查找id值为“header”的元素
$(".editor-label") 查找class名为“.editor-label的所有元素”
$("div") 查找所有<div>元素
$("#header div") 查找id值为“header”元素的所有后代<div>元素
$("#header>div") 查找id值为“header”元素的所有子<div>元素
$("a:even") 查找编号为偶数的锚标签

“#album-list img”用来选择id为album-list的<img>标签。

作为选择器的字符串看起来像层叠样式表(Cascading Style Sheet,CSS)中的项,是因为jQuery选择器的语法派生自CSS3.0选择器的语法,并在其基础上做了一些补充。

1.3 jQuery事件

jQuery的另一个优势在于,它提供了用来订阅DOM中事件的API。尽管使用一个通用的on方法可以捕获指定名称的任何事件,但jQuery也为一般的事件提供了专门的方法,比如click、blur和submit

可以通过传进一个函数来告知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类等。下面的代码演示了当用户的鼠标移过元素时,如何向一个页面上的锚标签添加或从中删除highlight类。当用户在标签上移动鼠标时,锚标签就会改变外观(假如有一个合适的highlight样式设置):

 $("a").mouseover(function(){
     $(this).addClass("highlight");
 }).mouseout(function(){
     $(this).removeClass("highlight");
 })

注意:

  • 代码中用到的所有依赖于封装集的jQuery方法,像mouseover方法,都返回同样的jQuery封装集。这就是说可以继续在选择的元素上调用jQuery方法,而不用再重新选择这些元素。这称为方法链
  • 许多常用操作在jQuery中都有与其对应的捷径方法(shortcut)。设置mouseover和mouseout效果是一种常见的操作,切换样式类型也是一种常见的操作。可以使用jQuery捷径方法重写上面的代码:
 $("a").hover(function(){
     $(this).toggleClass("highlight");
 })

jQuery包含了向Web服务器回发异步请求所需要的所有功能。可以利用jQuery来生成POST请求或GET请求,并且当请求完成(或出现错误)时jQuery会发出通知。

非侵入式JavaScript:

在Web早期阶段,jQuery出现以前,在同一个文件中混杂JavaScript代码和HTML标记是非常流行的做法。将JavaScript代码作为某个特性的值放入HTML元素中是很常见的,如以下代码:

<div onclick="javascript:alert('click');">Testing, testing</div>

当时在标记中嵌入JavaScript代码,是因为没有更简单的方法可以用来捕获单击事件,尽管嵌入的JavaScript代码可以实现事件捕获,但是这样的代码不够简洁。jQuery改变了这种情况,因为jQuery提供了查找元素和捕获单击事件更好的方法。现在可以从HTML特性中移除JavaScript代码了。事实上,可以将javascript代码与HTML完全分离。

非侵入式javascript很好的实践了javascript代码和标记的分离,可将所有需要的脚本代码打包到js文件中。如果查看视图的源代码,将不会看到有javascript代码嵌入在标记中。即使查看视图渲染的HTML标记,也看不到任何的javascript代码,脚本留下的唯一的痕迹是一个或多个引用javascript文件的<script>标签。

 1.4 jQuery的用法

     因为jQuery非常常用,站点布局(/Views/Shared/_Layout.cshtml)的footer部分包含了一个jQuery脚本引用。因此,默认情况下,站点的任何视图中都可以使用jQuery。在没有使用默认布局的任何视图中,或者如果我们在站点布局中删除了jQuery脚本引用,添加jQuery脚本引用和删除jQuery脚本引用是很容易的,只需要直接脚本引用或者使用预配置的jQuery捆绑。

要添加脚本引用,可包含如下的代码:

<script src="~Scripts/jquery-1.10.2.js"></script>

注意:ASP,NET MVC的Razor视图引擎会把这里的~操作符解析为当前网站的根目录,即便它出现了src特性。另外,HTML5不需要指定类型特性为text/javascript。

但这种方法依赖于版本,更好的在驶入中包含jQuery引用的方法是使用内置的、版本无关的jQuery脚本捆绑。

 @Scripts.Render(~/bundles/jquery)

上面的调用将渲染/App_Start/BundleConfig.cs中预定义的“jquery”脚本捆绑,这种捆绑利用了ASP.NET中的捆绑和微小特性,该特性利用版本号中包含的通配符匹配,自动优先使用jQuery的轻量版本。

  public static void RegisterBundles(BundleCollection bundles)
  {
     bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                 "~/Scripts/jquery-{version}.js"));

         //......
  }

2.1 自定义脚本

自定义的JavaScript代码,一般放置在“Scripts/App”目录下。

此处创建一个新的MusicScripts.js文件,并添加以下代码:

 $(function () {
     $("#album-list img").mouseover(function () {
         $(this).animate({ height: '+=25', width: '+=25' })
                .animate({ height: '-=25', width: '-=25' });
     });
 });

ASP.NET MVC5高级编程 之 Ajax

2.2 在节中放置脚本

 @section Scripts{
      <script src="~/Scripts/App/MusicScripts.js"></script>
  }

2.3 Scripts目录下的其他文件

  • Bootstrap.js:

包含一组基于jQuery的插件,他们通过添加额外的交互行为来增强Bootstrap。例如,Modals插件可显示简单的、使用Bootstrap样式的模态化界面,他使用jQuuery管理事件和动态页面显示。

  • Respond.js:

是一个很小的JavaScript库,包含的原因是因为Bootstrap要用到。

  • Modernizr.js:

通过改造老版本浏览器来帮助我们构建富有现代气息的应用程序。其中一个重要工作就是在老版本浏览器中启动新的HTML5元素(比如header、nav和menu),也可以检测特定浏览器是否支持一些高级功能,像定位位置(geolocation)和绘画画布(drawing canvas)。

 3 Ajax

3.1 Ajax辅助方法

ASP.NET MVC框架中包含一组Ajax辅助方法,他们可以用来创建表单和指向控制器操作的链接,但不同的是它们是异步进行的。当然使用这些辅助方法时,不需要编写任何脚本代码来实现程序的异步性。

这些Ajax辅助方法依赖于非侵入式MVC的jQuery扩展,如果使用这些辅助方法,需要引入脚本文件jquery.unobtrusive-ajax.js,并在视图中添加此脚本引用

 3.2 在项目中添加非侵入式Ajax脚本

Nuget --> Microsoft jQuery Unobtrusive Ajax --> 安装

ASP.NET MVC5高级编程 之 Ajax

脚本引用可以添加到程序的_Layout视图中,也可以仅添加到使用Ajax辅助方法的视图中。除非在网站中发出大量的Ajax请求,否则建议添加到单独的视图中。

脚本的添加可以拖拽。

 3.3 Ajax的ActionLink方法

与HTML辅助方法类似,Ajax属性上的大部分Ajax辅助方法都是扩展方法(出了AjaxHelper类型之外)。

Ajax属性的ActionLink方法可创建一个具有异步行为的锚标签,代码实现:

 1  <div id="dailydeal">
 2      @Ajax.ActionLink("Click here to see today's special!",
 3            "DailyDeal",
 4            null,
 5            new AjaxOptions
 6            {
 7                UpdateTargetId = "dailydeal",
 8                InsertionMode = InsertionMode.Replace,
 9                HttpMethod = "GET"
10            },
11            new {@class = "btn btn-primary"})
12  </div>

ActionLink

  • 第一个参数指定了链接文本;
  • 第二个参数是要异步调用的操作的名称,类似于同名的HTML辅助方法,Ajax辅助方法ActionLink也提供了各种重载版本,用来传递控制器的名称、路由值和HTML特性。
  • AjaxOptions参数指定了发送请求和处理服务器返回的结果的方式。参数中还包括用来处理错误、显示加载元素、显示确认对话框等的选项。

在这个示例中,AjaxOptions参数的选项指定了要使用来自服务器的响应元素来替换id值为“dailydeal”的元素

  • 最后的htmlAttributes参数指定了为链接使用的HTML类,以应用一个基本的Bootstrap按钮样式

为得到服务器的响应,需要在控制器的HomeController上添加一个DailyDeal操作:

  public ActionResult DailyDeal()
  {
      var album = GetDailyDeal();

      return PartialView("_DailyDeal",album);
  }

  private Album GetDailyDeal()
  {
      var album = storeDB.Albums
                  .OrderBy(a => System.Guid.NewGuid())
                  .First();
      album.Price *= 0.5m;
      return album;
  }

 Html5特性

非侵入式JavaScript的显著特点是在HTML中不包含任何JavaScript代码,也就是在HTML中看不到脚本代码。Ajax代码中指定的所有设置被编码成了HTML元素的特性,并且大多数的编码特性都有data-前缀,通常称为data-特性。

HTML5规范为私有应用程序状态保留了data-特性。换句话说,Web浏览器不会尝试解释data-特性的内容。因此可放心的把数据交给它,这些数据不会影响页面的显示或渲染。

向应用程序中添加jquery.unobtrusive-ajax文件的目的是查找特定的data-特性,然后操纵元素使其表现出不同行为。如果知道使用jQuery可以很容易地查找元素,那么非侵入式JavaScript文件中出现如下所示的代码也就很容易理解了

 $(function)(){
     $("a[data-ajax]=true").  //do something
 });

这段代码将使用jQuery查找data-ajax特性值为true的所有锚元素。元素上的data-ajax特性用来标识该元素需要实现异步行为。一旦非侵入式脚本识别了异步元素,它就可以读取该元素的其他设置(像替换模式、更新目标以及HTTP方法),还可以通过使用jQuery连接事件和发送请求来修改该元素的行为。

所有ASP.NET MVC Ajax特性都使用data-特性。

3.4 Ajax表单

使用Ajax在页面上放置一个异步表单

 1 <div class="panel panel-default">
 2     <div class="panel-heading">Artist search</div>
 3     <div class="panel-body">
 4         @using (Ajax.BeginForm("ArtistSearch", "Home",
 5            new AjaxOptions
 6            {
 7                InsertionMode = InsertionMode.Replace,
 8                HttpMethod = "GET",
 9                OnFailure = "searchFailed",
10                LoadingElementId = "ajax-loader",
11                UpdateTargetId = "searchresults",
12            }))
13         {
14             <input type="text" name="q" />
15             <input type="submit" value="search" />
16             <img id="ajax-loader"
17                  src="@Url.Content("~/Images/ajax-loader.gif")"
18                  style="display:none" />
19         }
20         <div id="searchresults"></div>
21     </div>
22 </div>
  • 当用户单击提交按钮时,浏览器就会向控制器HomeController的ArtistSearch操作发送异步的GET请求
  • LoadingElementId作为其中的一个选项。当执行异步请求时,客户端框架会自动的显示这个元素。此处为一个等待的动画。
  • OnFailure选项包括许多参数,可以设置这些参数以捕获来自Ajax请求的各种客户端事件,如OnBegin、OnComplete、OnSuccess和OnFailure等。可以给这些参数赋予一个JavaScript函数的名称,当事件触发时,调用该函数

上面的代码为OnFailure事件指定了一个名为searchFailed的函数,因此需要使用运行时能够访问到这个函数。此处将函数存放在MusicScript.js文件中。

 function searchFailed() {
     $("#searchresults").html("Sorry, there was a problem with the search.");
 }

如果服务器返回一个错误,就意味着Ajax辅助方法执行失败,此时可以捕获OnFailure事件。

  • 最后,当用户单击提交按钮提交表单时,服务器会收到一个Ajax请求,并可能以任意格式的内容做出响应。当客户端收到来自服务器端的响应时,非侵入式脚本就会将相应内容放入DOM中。

此处,新内容要替换的是一个id值为searchresults的元素

控制器中的代码为:

    public ActionResult ArtistSearch(string q)
    {
        var artists = GetArtists(q);

        return PartialView("_ArtistSearch", artists);
    }

    private List<Artist> GetArtists(string searchString)
    {
        return storeDB.Artists
              .Where(a => a.Name.Contains(searchString))
              .ToList();
    }

渲染的部分视图利用模型构建列表。该部分视图的名称是ArtistSearch.cshtml,位于项目的Views/Home文件夹下。

 @model IEnumerable<MvcMusicStore.Models.Artist>

 <div id="searchresults">
     <ul>
         @foreach (var item in Model)
         {
             <li>@item.Name</li>
         }
     </ul>
 </div>

4客户端验证

对于数据注解特性来说,ASP.NET MVC框架的客户端验证是默认开启的,下面介绍Album类的Title和Price属性:

     [Required]
     [StringLength(, MinimumLength = )]

     public string Title { get; set; }

     [Required]
     [Range(0.01, 100.00)]

     [DataType(DataType.Currency)]
     public decimal Price { get; set; }

数据注解特性使得这两个属性值必须键入,并且还对Title属性值限定了长度,对Price属性值范围限定了范围。ASP.NET MVC的模型绑定器在设置这些属性时会执行服务器端验证。这些内置的特性也触发客户端验证。客户端验证依赖于jQuery验证插件。

4.1 jQuery验证

jQuery验证插件(jquery.validate)默认情况下在MVC5应用程序项目的Scripts文件夹下。如果想实现客户端验证,那么在相应的视图中就需要包含jqueryval捆绑的引用。如果引用放到_Layout中,所有的视图上都会加载脚本,而不是仅在需要jQuery验证的视图上加载脚本,性能会受到影响。

 4.2 自定义验证

编写的MaxWordsAttribute验证特性代码如下:

   public clsaa MaxWordsAttribute : ValidationAttribute
   {
         public MaxWordsAttribute(int maxWords) : base("{0} has too many words.")
         {
          _maxWords = maxWords;
         }

         protected override ValidationResult IsValid(object value,
                                                        ValidationContext validationContext)
         {
            var valueAsString = value.ToString();
            if( valueAsString.Split(' ').Length > _maxWords)
            {
               var errorMessage = FormatErrorMessage(validationContext.DisplayName);
               return new ValidationResult(errorMessage);
             }
            return ValidationResult.Success;
         }
        private readonly int _maxWords;
   }

该特性只支持服务器端的验证,使用方法如下:

     [MaxWords()]
     public string Title { get; set; }

4.2.1 IClientValidatable

IClientValidatable接口定义了单个方法:GetIClientValidationRules。当ASP.NET MVC框架使用这个接口查找验证对象时,它会调用GetClientValidationRules方法来检索ModelClientValidationRule对象序列。这些对象携带有框架发送给客户端的元数据和规则。

可使用下面的代码为自定义验证器实现该接口:

 public clsaa MaxWordsAttribute : ValidationAttribute,IClientValidatable
      {
         public MaxWordsAttribute(int maxWords) : base("{0} has too many words.")
         {
              _maxWords = maxWords;
         }
         public int WowrdCount { get; set; }
         protected override ValidationResult IsValid(object value, ValidationContext validationContext)
         {
            var valueAsString = value.ToString();
            if( valueAsString.Split(' ').Length > _maxWords)
            {
               var errorMessage = FormatErrorMessage(validationContext.DisplayName);
               return new ValidationResult(errorMessage);
             }
            return ValidationResult.Success;
         }

         public IEnumerable<ModelClientValidationRule> GetClientValidationRules(MdelMetadata metadata,ControllerContext context)
         {
             var rule = new ModelClientValidationRule();
             rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
             rule.ValidationParameters.Add("wordcount", WordCount);
             rule.ValidationType = "maxwords";
             yield return rule;
         }
      }

要实现客户端验证,需要提供如下几点信息:

  • 如果验证失败,要显示的提示消息
  • 允许的单词数的范围
  • 一段用来计算单词数量的JavaScript代码标识。

这些信息就是嗲吗放进返回规则中的内容。请注意,如果需要在客户端触发多种类型的验证,代码可以返回多个规则。

ASP.NET MVC框架在客户端上利用GetClientValidationRules方法返回的规则将信息序列化为data-特性。