[翻译]ASP.NET MVC 3 开发的20个秘诀(十三)[20 Recipes for Programming MVC 3]:实现Ajax增强用户体验
2011-12-18 16:57
901 查看
议题
单击链接的时候,由于网页刷新内容需要重新加载,会感觉整个过程变的非常缓慢,尤其在我们只需要更新少量内容的时候感觉更加明显。
解决方案
修改之前的示例,将Html.ActionLink 创建的链接更改为由Ajax辅助类Ajax.ActionLink创建,使链接点击时只加载需要改变的内容。
讨论
MVC框架提供了几个非常棒的辅助类。在目前为止本书使用最后的就是HtmlHelper类,几乎之前所有的视图类都至少使用过一次。在这个秘诀中,将使用AjaxHelper辅助类替换Books和Index视图中所有的HtmlHelper辅助类。
要想实现Ajax视需要一点额外的设置才能使用的。通常情况下,也正是这点儿额外的工作阻止了开发人员使用它。但是我认为花费额外的时间去实现它是值得的,因为这将使用户在使用时获得非常好的体验。
在程序的配置文件Web.config文件中,设置ClientValidationEnabled和UnobtrusiveJavaScriptEnabled值为True:
完成最后的配置步骤,需要包含几个Javascript文件。这些文件需要在各个视图中共享使用,在Views/Shared/_Layout.cshtml文件中,将这两个Javascript文件包含到<head>标记中:
将这些文件自动嵌入到MVC3应用程序中,也就完成了Ajax核心功能的配置。接下来,我们需要修改Books/Index视图。在下面的示例中,修改了三个过滤器链接和列首的排序链接,用Ajax.ActionLink替换Html.ActionLink:
在这里最关键的是ActionLink方法最后的一个参数AjaxOptions。当用户点击这个Ajax链接时,Ajax请求会更新ID为“main”的HTML标签。如果仔细看了之前更改的共享布局的页面,会发现它包含一个ID为"main"的标记。事实上,这个是@RenderBody()方法在视图中输出的内容的容器。
同时我们在视图的顶部做了一个非常重要的检测,如果Ajax请求完成,则将Layout属性设置为空。这是一个非常重要的设置,因为Ajax请求并不是只是包含视图的内容,而是会包含整个布局的内容,这样就会将整个布局再次放入到当前布局中。
为了完成这个示例,需要将Shared/Pageing视图修改为使用AjaxHelper类实现:
现在,当用户点击更改书籍列表的链接,更新书籍列表整个页面将不会重新载入,这样为用户提供了更快、更好的用户体验。
此外,如果客户端不支持Javascript(例如,当搜索引擎检索访问时),链接依然可以正常工作,即使是用户禁止了Javascript或者搜索引擎仍然可以通过链接访问当一个正常的完整的页面重载的内容。
参考
AjaxHelper 原书地址 书籍源代码
单击链接的时候,由于网页刷新内容需要重新加载,会感觉整个过程变的非常缓慢,尤其在我们只需要更新少量内容的时候感觉更加明显。
解决方案
修改之前的示例,将Html.ActionLink 创建的链接更改为由Ajax辅助类Ajax.ActionLink创建,使链接点击时只加载需要改变的内容。
讨论
MVC框架提供了几个非常棒的辅助类。在目前为止本书使用最后的就是HtmlHelper类,几乎之前所有的视图类都至少使用过一次。在这个秘诀中,将使用AjaxHelper辅助类替换Books和Index视图中所有的HtmlHelper辅助类。
要想实现Ajax视需要一点额外的设置才能使用的。通常情况下,也正是这点儿额外的工作阻止了开发人员使用它。但是我认为花费额外的时间去实现它是值得的,因为这将使用户在使用时获得非常好的体验。
在程序的配置文件Web.config文件中,设置ClientValidationEnabled和UnobtrusiveJavaScriptEnabled值为True:
<?xml version="1.0"?> <configuration> <connectionStrings> <add name="ApplicationServices" connectionString= "data source=.\SQLEXPRESS;Integrated Security=SSPI; AttachDBFilename=|DataDirectory|aspnetdb.mdf; User Instance=true" providerName="System.Data.SqlClient"/> </connectionStrings> <appSettings> <add key="webpages:Version" value="1.0.0.0" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> <add key="smtpServer" value="localhost" /> <add key="smtpPort" value="25" /> <add key="smtpUser" value="" /> <add key="smtpPass" value="" /> </appSettings> <system.web> <compilation debug="true" targetFramework="4.0"> <assemblies> <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> </assemblies> </compilation> ... </system.web> ... </configuration>
完成最后的配置步骤,需要包含几个Javascript文件。这些文件需要在各个视图中共享使用,在Views/Shared/_Layout.cshtml文件中,将这两个Javascript文件包含到<head>标记中:
<!DOCTYPE html> <html> <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> <body> <div class="page"> <div id="header"> <div id="title"> <h1>My MVC Application</h1> </div> <div id="logindisplay"> @Html.Partial("_LogOnPartial") [ @Html.ActionLink("English", "ChangeLanguage", "Home", new { language = "en" }, null) ] [ @Html.ActionLink("Français", "ChangeLanguage", "Home", new { language = "fr" }, null) ] </div> <div id="menucontainer"> <ul id="menu"> <li> @Html.ActionLink("Home", "Index", "Home") </li> <li> @Html.ActionLink("About", "About", "Home") </li> </ul> </div> </div> <div id="main"> @RenderBody() </div> <div id="footer"> </div> </div> </body> </html>
将这些文件自动嵌入到MVC3应用程序中,也就完成了Ajax核心功能的配置。接下来,我们需要修改Books/Index视图。在下面的示例中,修改了三个过滤器链接和列首的排序链接,用Ajax.ActionLink替换Html.ActionLink:
@model PagedList.IPagedList<MvcApplication4.Models.Book> @if (IsAjax) { Layout = null; } <h2>@MvcApplication4.Resources.Resource1.BookIndexTitle</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链接时,Ajax请求会更新ID为“main”的HTML标签。如果仔细看了之前更改的共享布局的页面,会发现它包含一个ID为"main"的标记。事实上,这个是@RenderBody()方法在视图中输出的内容的容器。
同时我们在视图的顶部做了一个非常重要的检测,如果Ajax请求完成,则将Layout属性设置为空。这是一个非常重要的设置,因为Ajax请求并不是只是包含视图的内容,而是会包含整个布局的内容,这样就会将整个布局再次放入到当前布局中。
为了完成这个示例,需要将Shared/Pageing视图修改为使用AjaxHelper类实现:
<p> @if (Model.HasPreviousPage) { @Ajax.ActionLink("<< First", "Index", new { page = 1, sortOrder = ViewBag.CurrentSortOrder, filter = ViewBag.CurrentFilter, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" }) @Html.Raw(" "); @Ajax.ActionLink("< Prev", "Index", new { page = Model.PageNumber - 1, sortOrder = ViewBag.CurrentSortOrder, filter = ViewBag.CurrentFilter, Keyword = ViewBag.CurrentKeyword }, 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, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" }) @Html.Raw(" "); @Ajax.ActionLink("Last >>", "Index", new { page = Model.PageCount, sortOrder = ViewBag.CurrentSortOrder, filter = ViewBag.CurrentFilter, Keyword = ViewBag.CurrentKeyword }, new AjaxOptions { UpdateTargetId = "main" }) } else { @:Next > @Html.Raw(" ") @:Last >> } </p>
现在,当用户点击更改书籍列表的链接,更新书籍列表整个页面将不会重新载入,这样为用户提供了更快、更好的用户体验。
此外,如果客户端不支持Javascript(例如,当搜索引擎检索访问时),链接依然可以正常工作,即使是用户禁止了Javascript或者搜索引擎仍然可以通过链接访问当一个正常的完整的页面重载的内容。
参考
AjaxHelper 原书地址 书籍源代码
相关文章推荐
- [翻译]ASP.NET MVC 3 开发的20个秘诀(三)[20 Recipes for Programming MVC 3]:验证用户输入
- [翻译]ASP.NET MVC 3 开发的20个秘诀(十四)[20 Recipes for Programming MVC 3]:使用Ajax提交Form
- [翻译]ASP.NET MVC 3 开发的20个秘诀(十九)[20 Recipes for Programming MVC 3]:路由用户至特定的Controller或Action
- [翻译]ASP.NET MVC 3 开发的20个秘诀(四)[20 Recipes for Programming MVC 3]:实现多语言支持
- [翻译]ASP.NET MVC 3 开发的20个秘诀(八)[20 Recipes for Programming MVC 3]:对列表进行分页
- [翻译]ASP.NET MVC 3 开发的20个秘诀(九)[20 Recipes for Programming MVC 3]:对列表进行筛选
- [翻译]ASP.NET MVC 3 开发的20个秘诀(二)[20 Recipes for Programming MVC 3]:自动生成控制器和视图
- (转)[翻译]ASP.NET MVC 3 开发的20个秘诀(十八)[20 Recipes for Programming MVC 3]:自动完成搜索
- [翻译]ASP.NET MVC 3 开发的20个秘诀(五)[20 Recipes for Programming MVC 3]:发送欢迎邮件
- [翻译]ASP.NET MVC 3 开发的20个秘诀(六)[20 Recipes for Programming MVC 3]:找回忘记的密码
- [翻译]ASP.NET MVC 3 开发的20个秘诀(十五)[20 Recipes for Programming MVC 3]:启用图片验证码(CAPTCHA)
- [翻译]ASP.NET MVC 3 开发的20个秘诀(十二)[20 Recipes for Programming MVC 3]:缩放图片尺寸创建缩略图
- [翻译]ASP.NET MVC 3 开发的20个秘诀(二十)[20 Recipes for Programming MVC 3]:缓存结果数据加速页面载入
- [翻译]ASP.NET MVC 3 开发的20个秘诀(十七)[20 Recipes for Programming MVC 3]:卷帘式分页加载
- [翻译]ASP.NET MVC 3 开发的20个秘诀(十八)[20 Recipes for Programming MVC 3]:自动完成搜索
- [翻译]ASP.NET MVC 3 开发的20个秘诀(十六)[20 Recipes for Programming MVC 3]:站点移动化
- [翻译]ASP.NET MVC 3 开发的20个秘诀(十一)[20 Recipes for Programming MVC 3]:通过表单上传文件
- [翻译]ASP.NET MVC 3 开发的20个秘诀(一)[20 Recipes for Programming MVC 3]:通过密码验证限制访问视图
- [翻译]ASP.NET MVC 3 开发的20个秘诀(七)[20 Recipes for Programming MVC 3]:对列表进行排序
- [翻译]ASP.NET MVC 3 开发的20个秘诀(十)[20 Recipes for Programming MVC 3]:通过关键字进行列表搜索