[翻译]ASP.NET MVC 3 开发的20个秘诀

议题

单击链接的时候,由于网页刷新内容需要重新加载,会感觉整个过程变的非常缓慢,尤其在我们只需要更新少量内容的时候感觉更加明显。

解决方案

修改之前的示例,将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 >
<div >
<h1>My MVC Application</h1>
</div>
<div >
@Html.Partial("_LogOnPartial")
[ @Html.ActionLink("English", "ChangeLanguage",
"Home", new { language = "en" }, null) ]
[ @Html.ActionLink("Français", "ChangeLanguage",
"Home", new { language = "fr" }, null) ]
</div>
<div >
<ul >
<li>
@Html.ActionLink("Home", "Index", "Home")
</li>
<li>
@Html.ActionLink("About", "About", "Home")
</li>
</ul>
</div>
</div>
<div >
@RenderBody()
</div>
<div >
</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
}
&nbsp; | &nbsp;
@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
}
&nbsp; | &nbsp;
@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("&nbsp;");
@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("&nbsp;");
@:< Prev
}
&nbsp;&nbsp;
@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("&nbsp;");
@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("&nbsp;")
@:Last >>
}
</p>

复制代码

现在,当用户点击更改书籍列表的链接,更新书籍列表整个页面将不会重新载入,这样为用户提供了更快、更好的用户体验。

此外,如果客户端不支持Javascript(例如,当搜索引擎检索访问时),链接依然可以正常工作,即使是用户禁止了Javascript或者搜索引擎仍然可以通过链接访问当一个正常的完整的页面重载的内容。

参考

AjaxHelper原书地址书籍源代码

ASP.NET MVC

[翻译]ASP.NET MVC 3 开发的20个秘诀(十三)[20 Recipes for Programming MVC 3]:实现Ajax增强用户体验

posted @ 2011-12-18 16:57 O2DS 阅读(335) | 评论 (0)编辑

[翻译]ASP.NET MVC 3 开发的20个秘诀(十二)[20 Recipes for Programming MVC 3]:缩放图片尺寸创建缩略图

posted @ 2011-12-17 10:17 O2DS 阅读(835) | 评论 (2)编辑

[翻译]ASP.NET MVC 3 开发的20个秘诀(十一)[20 Recipes for Programming MVC 3]:通过表单上传文件

posted @ 2011-12-15 23:17 O2DS 阅读(951) | 评论 (2)编辑

[翻译]ASP.NET MVC 3 开发的20个秘诀(十)[20 Recipes for Programming MVC 3]:通过关键字进行列表搜索

posted @ 2011-12-15 10:32 O2DS 阅读(961) | 评论 (3)编辑

[翻译]ASP.NET MVC 3 开发的20个秘诀(九)[20 Recipes for Programming MVC 3]:对列表进行筛选

posted @ 2011-12-14 12:33 O2DS 阅读(1011) | 评论 (0)编辑

[翻译]ASP.NET MVC 3 开发的20个秘诀(八)[20 Recipes for Programming MVC 3]:对列表进行分页

posted @ 2011-12-13 16:15 O2DS 阅读(757) | 评论 (2)编辑

[翻译]ASP.NET MVC 3 开发的20个秘诀(七)[20 Recipes for Programming MVC 3]:对列表进行排序

posted @ 2011-12-12 13:50 O2DS 阅读(1152) | 评论 (0)编辑

[翻译]ASP.NET MVC 3 开发的20个秘诀(六)[20 Recipes for Programming MVC 3]:找回忘记的密码

posted @ 2011-12-10 20:00 O2DS 阅读(835) | 评论 (0)编辑

[翻译]ASP.NET MVC 3 开发的20个秘诀(五)[20 Recipes for Programming MVC 3]:发送欢迎邮件

posted @ 2011-12-09 09:32 O2DS 阅读(1304) | 评论 (9)编辑

[翻译]ASP.NET MVC 3 开发的20个秘诀(四)[20 Recipes for Programming MVC 3]:实现多语言支持

posted @ 2011-12-07 11:19 O2DS 阅读(923) | 评论 (2)编辑

[翻译]ASP.NET MVC 3 开发的20个秘诀(三)[20 Recipes for Programming MVC 3]:验证用户输入

posted @ 2011-12-06 22:16 O2DS 阅读(1198) | 评论 (0)编辑

[翻译]ASP.NET MVC 3 开发的20个秘诀(二)[20 Recipes for Programming MVC 3]:自动生成控制器和视图

posted @ 2011-12-03 23:31 O2DS 阅读(1122) | 评论 (2)编辑

[翻译]ASP.NET MVC 3 开发的20个秘诀(一)[20 Recipes for Programming MVC 3]:通过密码验证限制访问视图

posted @ 2011-11-29 12:04 O2DS 阅读(1880) | 评论 (7)编辑