Ajax (Asynchronous JavaScript and XML 的缩写),如咱们所见,这个概念的重点已经再也不是XML部分,而是 Asynchronous 部分,它是在后台从服务器请求数据的一种模型。MVC 框架内置了对 Unobtrusive Ajax 的支持,它容许咱们经过 MVC 的 Help mothod 来定义 Ajax 的特性,而不用在 View 中参杂一大段 JavaScript 代码。javascript
本文目录:html
在讲 MVC 中的 Unobtrusive Ajax 以前,咱们先来看看 MVC 中 Ajax 的普通使用方式,读者能够在阅读后文的时候进行比较学习。
新建一个MVC应用程序(基本模板),添加一个名为 Home 的 controller,为自动生成的 Index action 添加视图,编辑 Index.cshtml 代码以下:java
@{ ViewBag.Title = "Index"; } <script type="text/javascript"> function test() { $.ajax({ url: '@Url.Action("GetTestData")', type: "POST", success: function (result) { $("#lblMsg").text(result.msg); } }); } </script> <h2 id="lblMsg"></h2> <input type="button" value="测试" onclick="test();" />
在 HomeController 中添加一个名为 Test 的 action,以下:jquery
public JsonResult GetTestData() { return Json( new { msg = "Datetime from server:" + DateTime.Now.ToString("HH:mm:ss") } ); }
运行程序,点击测试按钮,咱们能够看到用 Ajax 从后台取回来的时间:ajax
每次点击测试按钮时间都会刷新。这个地方有一点须要提醒你们,这个例子中 $.ajax() 方法使用的是 POST 请求,若是要使用 GET 请求,Test action 中调用 Json 方法须要设置 JsonRequestBehavior 的值为 AllowGet(默认是 DenyGet),以下:浏览器
public JsonResult GetTestData() { return Json( new { msg = "Datetime from server:" + DateTime.Now.ToString("HH:mm:ss") }, JsonRequestBehavior.AllowGet ); }
另外,改为 GET 请求后,屡次点击测试按钮,时间不会刷新。这是由于 GET 请求在 ASP.NET 中对于相同的URL请求返回的是缓存中的数据。缓存
Unobtrusive Ajax 是在 Web 页面使用 JavaScript 的一种通用方式。这个术语没有明确的定义,但它有以下基本的原则(来自维基百科):服务器
为了加深理解,请观察以下某个 Unobtrusive Ajax 的“结构”部分的一段代码:app
... <form action="/People/GetPeopleData" data-ajax="true" data-ajax-mode="replace" data-ajax-update="#tableBody" id="form0" method="post"> ...
这是 MVC 开启 Unobtrusive JavaScript 后调用 Ajax.BeginForm 方法生成的代码。这段代码和 JavaScript 是彻底分离的,Html标签经过一些标记来告诉 JavaScript 所具备什么样的行为。分离出来的 JavaScript 文件(MVC中指引入的jquery.unobtrusive-ajax.min.js文件)中的代码,没有一句是专门为某个特定的Web页面中的某个Html元素来编写的,即全部函数都是通用的。这就是 Unobtrusive Ajax 的核心思想。框架
相对于普通使用 Ajax 的方式,Unobtrusive Ajax 更容易阅读,加强了可扩展性和一致性,并且方便维护。
在 MVC 中使用 Unobtrusive Ajax ,首先要将其“开启”,须要作两个动做。一个是配置根目录下的 Web.config 文件,在 configuration/appSettings 节点下的 UnobtrusiveJavaScriptEnabled 值设为 true,以下所示:
... <configuration> <appSettings> ... <add key="UnobtrusiveJavaScriptEnabled" value="true" /> </appSettings> </configuration> ...
UnobtrusiveJavaScriptEnabled 的值在程序建立的时候默认为true,在开发的时候有时候只须要检查一下。第二个动做就是在须要使用 MVC Unobtrusive Ajax 的 View 中引入jquery库和jquery.unobtrusive-ajax.min.js文件,通常更为常见的是在 /Views/Shared/_Layout.cshtml 中引入,以下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <script src="~/Scripts/jquery-1.8.2.min.js"></script> <script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script> </head> <body> @RenderBody() </body> </html>
如今咱们来作一个使用 Unobtrusive Ajax 的例子,从服务器获取一个简单的用户列表。为此咱们须要准备一个Model,以下:
namespace MvcApplication1.Models { public class Person { public string ID { get; set; } public string Name { get; set; } public Role Role { get; set; } } public enum Role { Admin, User, Guest } }
我通常习惯先写后台方法,再写UI。建立一个名为 People 的 controller, 在该 controller 中写好要用的 action,代码以下:
public class PeopleController : Controller { public class PeopleController : Controller { private Person[] personData = { new Person {ID = "ZhangSan", Name = "张三", Role = Role.Admin}, new Person {ID = "LiSi", Name = "李四", Role = Role.User}, new Person {ID = "WangWu", Name = "王五", Role = Role.User}, new Person {ID = "MaLiu", Name = "马六", Role = Role.Guest} }; public ActionResult Index() { return View(); } public PartialViewResult GetPeopleData(string selectedRole = "All") { IEnumerable<Person> data = personData; if (selectedRole != "All") { Role selected = (Role)Enum.Parse(typeof(Role), selectedRole); data = personData.Where(p => p.Role == selected); } return PartialView(data); } public ActionResult GetPeople(string selectedRole = "All") { return View((object)selectedRole); } } }
这里添加了 GetPeopleData action方法,根据 selectedRole 获取用户数据并传递给 PartialView 方法。
接着为 GetPeopleData action 建立一个partial view:/Views/People/GetPeopleData.cshtml ,代码以下:
@using MvcApplication1.Models @model IEnumerable<Person> @foreach (Person p in Model) { <tr> <td>@p.ID</td> <td>@p.Name</td> <td>@p.Role</td> </tr> }
再建立咱们的主视图 /Views/People/GetPeople.cshtml,代码以下:
@using MvcApplication1.Models @model string @{ ViewBag.Title = "GetPeople"; AjaxOptions ajaxOpts = new AjaxOptions { UpdateTargetId = "tableBody" }; } <h2>Get People</h2> <table> <thead><tr><th>First</th><th>Last</th><th>Role</th></tr></thead> <tbody id="tableBody"> @Html.Action("GetPeopleData", new { selectedRole = Model }) </tbody> </table> @using (Ajax.BeginForm("GetPeopleData", ajaxOpts)) { <div> @Html.DropDownList("selectedRole", new SelectList( new[] { "All" }.Concat(Enum.GetNames(typeof(Role))))) <button type="submit">Submit</button> </div> }
先是建立了一个 AjaxOptions 对象,经过它的一些属性(如UpdateTargetId、Url、HttpMethod等)可设置 Ajax 如何请求。这些属性可见名思意,如 UpdateTargetId 表示调用 Ajax 请求后要刷新的元素(经过元素ID来指定)。而后把须要提交到服务器的表单包括在 Ajax.BeginForm() 方法内,经过 submit 元素将该表单数据提交到服务器。
为了运行效果美观些,咱们在 _Layout.cshtml 文件中为 table 元素添加一些样式,以下:
... table, td, th { border: thin solid black; border-collapse: collapse; padding: 5px; background-color: lemonchiffon; text-align: left; margin: 10px 0; } ...
运行程序,URL 定位到 /People/GetPeople,在页面中点击提交按钮,效果以下:
Ajax.BeginForm 是经过提交表单的方式向服务器发送 ajax 请求,MVC中也可使用 Ajax.ActionLink() 方法生成连接来向服务器发送 ajax 请求。下面咱们在 GetPeople.cshtml 视图中增长这种请求方式:
<div> @foreach (string role in Enum.GetNames(typeof(Role))) { @Ajax.ActionLink(role, "GetPeopleData", new {selectedRole = role}, new AjaxOptions {UpdateTargetId = "tableBody"}) @: } </div>
效果和前面是同样的:
Ajax.ActionLink() 和 Ajax.BeginForm() 不一样的是,前者只能经过 Url 参数向服务器传送数据。
Unobtrusive Ajax 是如何工做的呢?
当调用 Ajax.BeginForm 方法后,经过 AjaxOptions 对象设置的属性将会被转化成 form 元素的属性(标记),这些属性以 data-ajax 开头,如本示例生成的 form 元素:
<form action="/People/GetPeopleData" data-ajax="true" data-ajax-mode="replace" data-ajax-update="#tableBody" id="form0" method="post"> ...
当 GetPeople.cshtml 视图加载完成并呈现 Html 页面时,jquery.unobtrusive-ajax.js 库寻找全部 data-ajax 属性值为true的元素,而后根据其余以 data-ajax 开头的属性值,jQuery 库中的函数将知道如何去执行 Ajax 请求。
AjaxOptions 类中的属性告诉 MVC 框架如何生成 Ajax 请求相关的 JavaScript 和 Html 代码。它包含以下属性:
这些属性 VS 的智能提示都有很好的解释,这里不一个一个讲,只选几个有表明性的讲讲。
在上面的示例中,咱们在 Ajax.BeginForm() 方中指定了 action 名称参数,MVC 帮咱们生成了Ajax请求的Url ( action="/People/GetPeopleData" )。这样作存在一个问题,当浏览器禁用JavaScript的时候,点击提交按钮页面将发生新的请求(非Ajax请求 /People/GetPeopleData),这样服务器返回的数据将直接替换掉原来的页面。解决这个问题可使用 AjaxOptions.Url 属性,缘由是 AjaxOptions.Url 属性会生成另一个专门用于 ajax 请求的Url。以下咱们对 /Views/People/GetPeople.cshtml 进行简单的修改:
... @{ ViewBag.Title = "GetPeople"; AjaxOptions ajaxOpts = new AjaxOptions { UpdateTargetId = "tableBody", Url = Url.Action("GetPeopleData") }; } ... @using (Ajax.BeginForm(ajaxOpts)) { ... }
运行后咱们看到的是和先前同样的结果,说明在效果上没有区别。但它生成的 form 属性却不同:
<form id="form0" action="/People/GetPeople" method="post" data-ajax-url="/People/GetPeopleData" data-ajax-update="#tableBody" data-ajax-mode="replace" data-ajax="true"> ...
它生成了两个 Url,分别为 action 属性 和 data-ajax-url 属性的值,前者是 Ajax.BeginForm() 方法根据当前 controller 和 action 名称生成的,后者是 AjaxOptions 的 Url 属性生成的。当浏览器没有禁用 JavaScript 时,Unobtrusive Ajax JS库会获取 data-ajax-url 属性的值做为 Url 发生 ajax 请求。当浏览器禁用了 JavaScript 时,天然 action 属性的值决定了表示提交的 Url,服务器将返回原来整个的页面。虽然局部未能刷新,但不会让用户以为网站作得很糟糕。
当加载数据须要花较长时间,为了不假死状态,应当给用户一个反馈信息,如“正在加载...”字样。在 MVC 的 Unobtrusive Ajax 中经过 AjaxOptions 的 LoadingElementId 和 LoadingElementDuration 两个属性可轻松作到这一点。修改 GetPeople.cshtml 以下:
@using MvcApplication1.Models @model string @{ ViewBag.Title = "GetPeople"; AjaxOptions ajaxOpts = new AjaxOptions { UpdateTargetId = "tableBody", Url = Url.Action("GetPeopleData"), LoadingElementId = "loading", LoadingElementDuration = 1000, }; } <h2>Get People</h2> <div id="loading" class="load" style="display:none"> <p>Loading Data...</p> </div> ...
不解释,运行程序看效果:
使用MVC中的 Unobtrusive Ajax 弹出确认对话框也很方便,设置一下 AjaxOptions.Confirm 属性的值却可,以下:
... @{ ViewBag.Title = "GetPeople"; AjaxOptions ajaxOpts = new AjaxOptions { UpdateTargetId = "tableBody", Url = Url.Action("GetPeopleData"), LoadingElementId = "loading", LoadingElementDuration = 1000, Confirm = "Do you wish to request new data?" }; } ...
弹出的对话框以下:
AjaxOptions 类中的 OnBegin、OnComplete、OnFailure 和 OnSuccess 属性容许咱们在 ajax 请求周期的某个状态点定义回调函数。来看具体的用法。
在 GetPeople.cshtml 文件中加入以下4个回调函数:
<script type="text/javascript"> function OnBegin() { alert("This is the OnBegin Callback"); } function OnSuccess(data) { alert("This is the OnSuccessCallback: " + data); } function OnFailure(request, error) { alert("This is the OnFailure Callback:" + error); } function OnComplete(request, status) { alert("This is the OnComplete Callback: " + status); } </script>
接着设置 AjaxOptions 对象的4个事件属性:
...
@{ ViewBag.Title = "GetPeople"; AjaxOptions ajaxOpts = new AjaxOptions { UpdateTargetId = "tableBody", Url = Url.Action("GetPeopleData"), OnBegin = "OnBegin", OnFailure = "OnFailure", OnSuccess = "OnSuccess", OnComplete = "OnComplete" }; }
...
运行程序,弹出三个消息框以下:
这四个事件属性中最经常使用的就是 OnSuccess 和 OnFailure 两个属性了,如咱们会常常在 OnSuccess 回调函数中对返回的 Json 数据进行处理。
其实我我的更倾向于普通的 Ajax 使用方式。Ajax.BeginForm() 和 Ajax.ActionLink() 用的少,习惯用 Html.BeginForm() 或 Html.ActionLink() 和手写 jQuery ajax 代码来代替。
参考:《Pro ASP.NET MVC 4 4th Edition》
做者:Liam Wang