ASP.NET MVC 4 (八) URL鏈接和Ajax幫助函數
使用幫助函數創建鏈接
MVC提供一些幫助函數創建鏈接,這些函數根據路徑映射表自動調整生成的URL:
| 說明 | 示例 | 輸出結果 |
| 應用程序相對URL | Url.Content("~/Content/Site.css") | /Content/Site.css |
| 到控制器action的鏈接 | Html.ActionLink("My Link", "Index", "Home") | <a href="/">My Link</a> |
| Action的URL | Url.Action("GetPeople", "People") | /People/GetPeople |
| 使用路徑映射的URL | Url.RouteUrl(new {controller = "People", action="GetPeople"}) | /People/GetPeople |
| 使用路徑映射的鏈接 |
Html.RouteLink("My Link", new {controller = "People", action="GetPeople"}) |
<a href="/People/GetPeople">My Link</a> |
| 命名路徑映射的鏈接 |
Html.RouteLink("My Link", "FormRoute", new {controller = "People", action="GetPeople"}) |
<a href="/app/forms/People/GetPeople">My Link</a> |
使用MVC Unobtrusive Ajax
MVC內建基于jQuery的unobtrusive Ajax的支持,之所以稱之為unobtrusive Ajax是因為不像常規Ajax那樣大量使用XML。要使用unobtrusive Ajax,首先需要在web.config的 configuration/appSettings一節開啟UnobtrusiveJavaScriptEnabled支持:
... <configuration> <!-- other elements omitted for brevity --> <appSettings> <add key="webpages:Version" value="2.0.0.0" /> <add key="webpages:Enabled" value="false" /> <add key="PreserveLoginUrl" value="true" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> </appSettings> <!-- other elements omitted for brevity --> </configuration> ...
同時我們需要引用相關的javascript文件,可以把對這些腳本文件的引用放到布局文件_layout.cshtml中:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
<link href="~/Content/Site.css" rel="stylesheet"/>
<script src="~/Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js" type="text/javascript"></script>
</head>
<body>
@RenderBody()
</body>
</html>
jquery-1.7.1.min.js為jQuery的核心庫,jquery.unobtrusive-ajax.min.js則提供Ajax功能(基于jquery庫),文件名中的.min表示幾乎不可能調試的縮減版本,可以在開發時使用非.min版本,發布時再采用.min版本。
使用Unobtrusive Ajax表單
下面以實例演示如何使用Ajax表單,從控制器開始:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using HelperMethods.Models;
namespace HelperMethods.Controllers
{
public class PeopleController : Controller
{
private Person[] personData = {
new Person {FirstName = "Adam", LastName = "Freeman", Role = Role.Admin},
new Person {FirstName = "Steven", LastName = "Sanderson", Role = Role.Admin},
new Person {FirstName = "Jacqui", LastName = "Griffyth", Role = Role.User},
new Person {FirstName = "John", LastName = "Smith", Role = Role.User},
new Person {FirstName = "Anne", LastName = "Jones", 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()方法根據選擇的角色過濾Person列表,返回一個分部視圖,對應的GetPeopleData.cshtml:
@using HelperMethods.Models
@model IEnumerable<Person>
@foreach (Person p in Model) {
<tr>
<td>@p.FirstName</td>
<td>@p.LastName</td>
<td>@p.Role</td>
</tr>
}
在Getpeople視圖中我們調用GetPeopleData()同時創建一個Ajax form:
@using HelperMethods.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>
}
Ajax.BeginForm()創建一個Ajax form,使用AjaxOptions對象作為參數,生成的HTML結果:
... <form action="/People/GetPeopleData" data-ajax="true" data-ajax-mode="replace" data-ajax-update="#tableBody"id="form0" method="post"> ...
瀏覽器請求GetPeople頁面時jquery.unobtrusive-ajax.js掃描data-ajax=true的單元,以此確認這是一個ajax的表單,點擊提交時不刷新整個頁面,而是用從/people/getpeopledata返回的結果替換tablebody的內容。
Ajax options
AjaxOptions控制向服務器異步請求時的方式,包含這些屬性:
| 屬性 | 說明 |
| Confirm | 在開始異步請求時向用戶顯示一條消息以確認 |
| HttpMethod | 設置請求的HTTP方法,必須是get或者post |
| InsertionMode | 如何嵌入服務器結果返回的HTML,可以是InsertAfter、InsertBefore、Replace(默認) |
| LoadingElementId | 指定Ajax請求時要顯示的Loading單元元素ID |
| LoadingElementDuration | Loading元素動畫顯示的時長 |
| UpdateTargetId | 請求返回結果要插入的元素ID |
| Url | Ajax表單提交的URL |
上面的GetPeople視圖Ajax form提交的URL是/People/GetPeopleData,如果用戶禁止了java腳本,提交form返回的結果會只是GetPeopleData分部視圖,我們可以直接在ajaxOptions指定ajax請求的URL來解決:
@using HelperMethods.Models
@model string
@{
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?"
};
}
<h2>Get People</h2>
<div id="loading" class="load" style="display:none">
<p>Loading Data...</p>
</div>
<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(ajaxOpts))
{
<div>
@Html.DropDownList("selectedRole", new SelectList(
new[] { "All" }.Concat(Enum.GetNames(typeof(Role)))))
<button type="submit">Submit</button>
</div>
}
生成的表單HTML:
... <form action="/People/GetPeople"data-ajax="true" data-ajax-mode="replace" data-ajax-update="#tableBody" data-ajax-url="/People/GetPeopleData"id="form0" method="post"> ...
這樣表單提交的URL依然是/People/GetPeople,即使禁止了java腳本返回的仍然是GetPeople頁面,ajax請求的URL通過data-ajax-url指定為/People/GetPeopleData。我們還設置 AjaxOptions.LoadingElementId為Loading,這是一個diplay:none風格的DIV元素,它只在AJAX異步請求時顯示一秒鐘(LoadingElementDuration = 1000)。AjaxOptions.Confirm= "Do you wish to request new data?" ,在每次Ajax請求時都會彈出網頁Message對話框詢問(對話框消息為這里設定的"Do you wish to request new data?")。
Ajax鏈接
上面的例子中我們使用表單提交數據,如果是使用鏈接做ajax異步請求可以這樣操作:
...
<div>
@foreach (string role in Enum.GetNames(typeof(Role))) {
<div class="ajaxLink">
@Ajax.ActionLink(role, "GetPeopleData",
new {selectedRole = role},
new AjaxOptions {UpdateTargetId = "tableBody"})
</div>
}
</div>
...
這里對role枚舉中每個元素生成一個鏈接,生成的鏈接元素類似:
... <a data-ajax="true" data-ajax-mode="replace" data-ajax-update="#tableBody" href="/People/GetPeopleData?selectedRole=Guest">Guest</a> ...
點擊某個鏈接時ajax返回的HTML數據會用于替換tableBody元素,和使用表單提交ajax請求效果一樣。同樣如果禁用了java腳本,返回的結果會只是getpeopledata分部視圖,我們可以這樣改進:
<div>
@foreach (string role in Enum.GetNames(typeof(Role))) {
<div class="ajaxLink">
@Ajax.ActionLink(role, "GetPeople",
new {selectedRole = role},
new AjaxOptions { UpdateTargetId = "tableBody", Url = Url.Action("GetPeopleData", new {selectedRole = role})
})
</div>
}
</div>
鏈接請求的地址是GetPeople,Ajax請求的URL則僅是GetPeopleData,請求的HTML結果作用于tablebody。
Ajax回調
AjaxOptions類暴露一組屬性允許我們指定一個java腳本函數,在ajax請求周期中調用這些腳本函數:
| 屬性 | jQuery事件 | 說明 |
| OnBegin | beforeSend | 在Ajax請求發送前調用 |
| OnComplete | complete | 請求成功時調用 |
| OnFailure | error | 請求失敗時調用 |
| OnSuccess | success | 無論請求成功與否都在請求完成時調用 |
結合回調函數我們可以對上面的例子進一步修改,首先修改控制器的GetPeopleData方法:
public ActionResult 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);
}
if (Request.IsAjaxRequest())
{
var formattedData = data.Select(p => new
{
FirstName = p.FirstName,
LastName = p.LastName,
Role = Enum.GetName(typeof(Role), p.Role)
});
return Json(formattedData, JsonRequestBehavior.AllowGet);
}
else
{
return PartialView(data);
}
}
我們使用Request.IsAjaxRequest判斷請求是否來自于ajax,它的依據是瀏覽器在ajax請求時在頭中會包含X-Requested-With=XMLHttpRequest。如果請求來自于ajax,控制器方法返回的是Json(data, JsonRequestBehavior.AllowGet)創建的JsonResult對象,默認JSON數據只在POST請求中發送,這里在第二個參數中指定JsonRequestBehavior.AllowGet允許在GET請求中使用JSON數據。MVC框架負責對formattedData做json封裝,封裝的格式由MVC嘗試使用最適宜的方法確定,這里返回的JSON數據類似:
...
{"PersonId":0,"FirstName":"Adam","LastName":"Freeman",
"BirthDate":"\/Date(62135596800000)\/","HomeAddress":null,"IsApproved":false,"Role":0}
...
視圖中我們在AjaxOptions的OnSucess回調函數中處理返回的JSON數據:
@using HelperMethods.Models
@model string
@{
ViewBag.Title = "GetPeople";
AjaxOptions ajaxOpts = new AjaxOptions
{
//UpdateTargetId = "tableBody",
Url = Url.Action("GetPeopleData"),
LoadingElementId = "loading",
LoadingElementDuration = 1000,
OnSuccess = "processData"
};
}
<script type="text/javascript">
function processData(data) {
var target = $("#tableBody");
target.empty();
for (var i = 0; i < data.length; i++) {
var person = data[i];
target.append("<tr><td>" + person.FirstName + "</td><td>"
+ person.LastName + "</td><td>" + person.Role + "</td></tr>");
}
}
</script>
<h2>Get People</h2>
<div id="loading" class="load" style="display: none">
<p>Loading Data...</p>
</div>
<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(ajaxOpts))
{
<div>
@Html.DropDownList("selectedRole", new SelectList(
new[] { "All" }.Concat(Enum.GetNames(typeof(Role)))))
<button type="submit">Submit</button>
</div>
}
<div>
@foreach (string role in Enum.GetNames(typeof(Role)))
{
<div class="ajaxLink">
@Ajax.ActionLink(role, "GetPeople",
new { selectedRole = role },
new AjaxOptions
{
Url = Url.Action("GetPeopleData", new { selectedRole = role }),
OnSuccess = "processData"
})
</div>
}
</div>
腳本函數processData在AjaxOptions的OnSuccess回調時調用,它負責處理請求返回的JSON數據,從中分拆出各個Person對象,根據這些數據直接改寫tableBody標簽的內容,因此我們不再需要在AjaxOptions通過UpdateTargetId指定要替換內容的元素。
以上為對《Apress Pro ASP.NET MVC 4》第四版相關內容的總結,不詳之處參見原版 http://www.apress.com/9781430242369。

浙公網安備 33010602011771號