[转载]【译】MVC3 20个秘方-(13)使用Ajax Helper 提高用户体验 – 技术弟弟 – 博客园.
问题
当你点击链接时,整个的网页都被重新加载。尤其是你仅仅一小点内容需要被更新时,这将被感觉是一个很慢的过程。
解决方案
更新之前创建的HTML.ActionLink 去调用ajax 帮助类。Ajax.ActionLink 仅仅去重新加载那些发生变化的内容。
讨论
MVC提供了几个给力的帮助类。到目前为止,这本书中已经广泛的应用了HTML Helper。在过去创建的所有view中,HTML helper至少都使用过一次。在这个秘方中,我们将使用AjaxHelper类替换掉Book/Index中的HtmlHelper 类。
实现Ajax需要一点额外的设置才可以使用。通常情况下我发现这个额外的工作,可以打消开发人员使用它的念头。我要让大家知道,额外的安装设置是值得的,因为带来的好处是获得了更好的用户体验,这是非常值得努力去做的。
步骤从web.config开始。2个关键的地方要被设置成true. ClientValidationEnabled 和UnobtrusiveJavaScriptEnabled。
译者:原书中代码引入了整个web.config文件。我们只需要到appSettings节点下即可找到这两个keys。
<appSettings> <add key="webpages:Version" value="1.0.0.0" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> </appSettings>
接下来的步骤是我们要引入几个JavaScript 文件。我们要在里shared 的layout文件夹里完成这件事,因为几乎我们创建所有的view时都会引用它(布局模板)。在Views/Shared /_Layout.cshtml 文件的<head>标签中。我们引入2个javascript 文件,代码如下:
<head> <title>@ViewBag.Title</title> <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" /> <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")"type="text/javascript"></script> </head>
这些文件被自动的包含在基础MVC3应用程序中。(译者:你可以从scripts文件夹中找到他们)。以上的步骤完成AJAX的核心配置。接下来, 我们要更新Book/index view。在下边的例子里,三个filter link和sortable header link将用Ajax.ActionLink 替换Html.ActionLink.代码如下:
@model PagedList.IPagedList<MvcApplication.Models.Book> @if (IsAjax) { Layout = null; } <h2> Title</h2> <p> @Html.ActionLink("Create New", "Create") </p> <p> Show: @if (ViewBag.CurrentFilter != "") { @Ajax.ActionLink("All", "Index", new { sortOrder = ViewBag.CurrentSortOrder, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" }) } else { @:All } | @if (ViewBag.CurrentFilter != "NewReleases") { @Ajax.ActionLink("New Releases", "Index", new { filter = "NewReleases", sortOrder = ViewBag.CurrentSortOrder, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" }) } else { @:New Releases } | @if (ViewBag.CurrentFilter != "ComingSoon") { @Ajax.ActionLink("Coming Soon", "Index", new { filter = "ComingSoon", sortOrder = ViewBag.CurrentSortOrder, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" }) } else { @:Coming Soon } </p> @using (Html.BeginForm()) { @:Search: @Html.TextBox("Keyword")<input type="submit" value="Search" /> } @Html.Partial("_Paging") <table> <tr> <th> @Ajax.ActionLink("Title", "Index", new { sortOrder = ViewBag.TitleSortParam, filter = ViewBag.CurrentFilter, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" }) </th> <th> @Ajax.ActionLink("Isbn", "Index", new { sortOrder = ViewBag.IsbnSortParam, filter = ViewBag.CurrentFilter, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" }) </th> <th> Summary </th> <th> @Ajax.ActionLink("Author", "Index", new { sortOrder = ViewBag.AuthorSortParam, filter = ViewBag.CurrentFilter, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" }) </th> <th> Thumbnail </th> <th> @Ajax.ActionLink("Price", "Index", new { sortOrder = ViewBag.PriceSortParam, filter = ViewBag.CurrentFilter, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" }) </th> <th> @Ajax.ActionLink("Published", "Index", new { sortOrder = ViewBag.PublishedSortParam, filter = ViewBag.CurrentFilter, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" }) </th> <th> </th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.Isbn) </td> <td> @Html.DisplayFor(modelItem => item.Summary) </td> <td> @Html.DisplayFor(modelItem => item.Author) </td> <td> @Html.DisplayFor(modelItem => item.Thumbnail) </td> <td> @Html.DisplayFor(modelItem => item.Price) </td> <td> @Html.DisplayFor(modelItem => item.Published) </td> <td> @Html.ActionLink("Edit","Edit", new { id = item.ID }) | @Html.ActionLink("Details","Details", new { id = item.ID }) | @Html.ActionLink("Delete","Delete", new { id = item.ID }) </td> </tr> } </table> @Html.Partial("_Paging")
译者:上边代码标绿的地方就是我们更新的地方。
我们做的关键的事就是在ActionLink的最后一个参数添加了AjaxOptions。 这意味着一旦用户点击了这个AJAX link,AJAX请求的结果将会更新id 是“main”的html元素。如果你看看share文件夹中早期我们修改的layout文件,你会注意到有一个<div>的id 是“main”。实际上,这个div就是@Renderbody()的容器,它是一个view输出的地方。
另外一个重要的事就是我们在view的顶端加了一个AJAX是否完成的检测。如果请求通过AJAX完成,layout设 置成null。这是一个非常重要的因素,因为如果它没完成,Ajax请求的结果将会包含不仅仅是view的result,也会包含整个layout,而没 必要再去替换掉layout了。
为了完成这个示例,share文件夹下的_Paging也要使用Ajax helper更新,代码如下:
<p> @if (Model.HasPreviousPage) { @Ajax.ActionLink("<< First", "Index", new { page = 1, sortOrder = ViewBag.CurrentSortOrder, filter = ViewBag.CurrentFilter }, new AjaxOptions { UpdateTargetId ="main" }) @Html.Raw(" "); @Html.ActionLink("< Prev", "Index", new { page = Model.PageNumber - 1, sortOrder = ViewBag.CurrentSortOrder, filter = ViewBag.CurrentFilter }, new AjaxOptions { UpdateTargetId ="main" }) } else { @:<< First @Html.Raw(" "); @:< Prev } @if (Model.HasNextPage) { @Ajax.ActionLink("Next >", "Index", new { page = Model.PageNumber + 1, sortOrder = ViewBag.CurrentSortOrder, filter = ViewBag.CurrentFilter }, new AjaxOptions { UpdateTargetId ="main" }) @Html.Raw(" "); @Ajax.ActionLink("Last >>", "Index", new { page = Model.PageCount, sortOrder = ViewBag.CurrentSortOrder, filter = ViewBag.CurrentFilter }, new AjaxOptions { UpdateTargetId ="main" }) } else { @:Next > @Html.Raw(" ") @:Last >> } </p>
现在,当用户点击一个link 改变book 列表时,整个的页面不会被重新载入并且仅仅是图书列表被更新。这提供了一个更好、更快的用户体验。
而且,如果客户端不支持javascript(例如,一个搜索引擎访问),这个link将像以前一样工作。它将重新载入整个页面。
另请参见