mvc驗證jquery.unobtrusive-ajax

Unobtrusive Ajax

Ajax (Asynchronous JavaScript and XML 的縮寫),如咱們所見,這個概念的重點已經再也不是XML部分,而是 Asynchronous 部分,它是在後臺從服務器請求數據的一種模型。MVC 框架內置了對 Unobtrusive Ajax 的支持,它容許咱們經過 MVC 的 Help mothod 來定義 Ajax 的特性,而不用在 View 中參雜一大段 JavaScript 代碼。javascript

本文目錄:html

普通 Ajax 使用方式

在講 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

Unobtrusive Ajax 是在 Web 頁面使用 JavaScript 的一種通用方式。這個術語沒有明確的定義,但它有以下基本的原則(來自維基百科):服務器

  • 行爲(JavaScript 代碼)與 Web 頁面的結構(Html 標記)和表現(CSS樣式)分離。
  • JavaScript 最佳實現,解決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

在 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"}) @:&nbsp;
    } 
</div>

效果和前面是同樣的:

Ajax.ActionLink() 和 Ajax.BeginForm() 不一樣的是,前者只能經過 Url 參數向服務器傳送數據。

Unobtrusive Ajax 如何工做

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

AjaxOptions 類中的屬性告訴 MVC 框架如何生成 Ajax 請求相關的 JavaScript 和 Html 代碼。它包含以下屬性:

這些屬性 VS 的智能提示都有很好的解釋,這裏不一個一個講,只選幾個有表明性的講講。

AjaxOptions.Url 屬性

在上面的示例中,咱們在 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,服務器將返回原來整個的頁面。雖然局部未能刷新,但不會讓用戶以爲網站作得很糟糕。

Ajax 加載數據的同時給用戶反饋

當加載數據須要花較長時間,爲了不假死狀態,應當給用戶一個反饋信息,如「正在加載...」字樣。在 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?" 
    };
}
...

彈出的對話框以下:

Ajax 回調函數

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

相關文章
相關標籤/搜索