原文:[ASP.NET MVC] 利用动态注入HTML的方式来设计复杂页面

随着最终用户对用户体验需求的不断提高,实际上我们很多情况下已经在按照桌面应用的标准来设计Web应用,甚至很多Web页面本身就体现为一个单一的页面。对于这种复杂的页面,我们在设计的时候不可以真的将所有涉及的元素通通至于某个单独的View中,将复杂页面相对独立的内容“分而治之”才是设计之道。我们可以借鉴Smart Clent应用的设计方式:将一个Windows Form作为应用的容器(Smart Client Shell),在操作过程中动态地激活相应的用户控件(Smart Part)并加载到容器中。对于一个复杂页面来说,我们也只需要将其设计成一个容器,至于运行过程中动态显示的内容则可以通过Ajax调用获取相应的HTML来填充。[源代码从这里下载]

目录
一、实例演示
二、作为容器的View
三、显示联系人列表
四、弹出“修改联系人”对话框
五、联系人信息的最终修改

一、实例演示

我们先来演示一个简单的例子,假设我们要设计一个“联系人”管理的页面。该页面初始状态如左图所示,它仅仅具有一个用于输入查询条件(First Name和Last Name)进行联系人查询的表单。当用户输入相应的查询条件之后点击“Retrieve”按钮,相应的联系人列表显示以表格的形式显示出来(中图)。当我们点击作为ID的链接后,会以“模态对话框”的形式显示当前联系人的编辑“窗口”(右图)。

这个“单页面应用”是通过ASP.NET MVC开发的,接下来我们来逐步介绍如果将同一页面中的这三块不同的内容提取出来进行“分而治之”。

二、作为容器的View

如下所示的是表示联系人的Contact类型的定义,没有什么特别之处:

   1: public class Contact

   2: {

   3:     [Required]

   4:     public string Id { get; set; }

   5:     [Required]

   6:     public string FirstName { get; set; }

   7:     [Required]

   8:     public string LastName { get; set; }

   9:     [Required]

  10:     [DataType(DataType.EmailAddress)]

  11:     public string EmailAddress { get; set; }

  12:     [Required]

  13:     [DataType(DataType.PhoneNumber)]

  14:     public string PhoneNo { get; set; }

  15: }

联系人管理对应的HomeController定义如下。简单起见,我们通过一个静态字段来表示维护的联系人列表。我们仅仅列出了默认的Action方法Index,它会直接将作为“容器页面”的View呈现出来。

   1: public class HomeController : Controller

   2: {

   3:     private static List<Contact> contacts = new List<Contact>

   4:     {

   5:         new Contact{Id = "001", FirstName = "San", LastName = "Zhang", EmailAddress = "zhangsan@gmail.com", PhoneNo="123"},

   6:         new Contact{Id = "002", FirstName = "Si", LastName = "Li", EmailAddress = "zhangsan@gmail.com", PhoneNo="456"}

   7:     };

   8:  

   9:     public ActionResult Index()

  10:     {

  11:         return View();

  12:     }    

  13:      //其他Action方法

  14: }

如下所示的是Index.cshtml的定义,在这里使用了Twitter的Bootstrap,所示我们引用了相应的CSS和JS。这个主体部分包含三个<div>,分别对应着上述的三个部分。

   1: <html>

   2:     <head>

   3:         <title>Contacts</title>

   4:         <link href="@Url.Content("~/Assets/css/bootstrap.css")" rel="stylesheet" type="text/css" />

   5:         <link href="@Url.Content("~/Assets/css/bootstrap-responsive.css")" rel="stylesheet" type="text/css" />

   6:     </head>

   7:     <body>

   8:         <div class="form-search">

   9:            @Html.Partial("QueryFormPartial")

  10:          </div>

  11:         <div id="contactList"></div>

  12:         <div class="modal fade" id="contactDialog"></div>

  13:  

  14:         <script type="text/javascript" src="@Url.Content("~/Assets/js/jquery-1.7.1.min.js")"></script>

  15:         <script type="text/javascript" src="@Url.Content("~/Assets/js/bootstrap.min.js")"></script>

  16:         <script type="text/javascript" src="@Url.Content("~/Assets/js/jquery.unobtrusive-ajax.min.js")"></script>

  17:         <script type="text/javascript" src="@Url.Content("~/Assets/js/jquery.validate.min.js")"></script>

  18:         <script type="text/javascript" src="@Url.Content("~/Assets/js/jquery.validate.unobtrusive.min.js")"></script>

  19:     </body>

  20: </html>

表示“查询表单”的部分定义在如下所示的Partial View(QueryFormPartial.cshtml),直接通过调用HtmlHelper的Partial方法呈现在当前View中。

   1: @{

   2:     Layout = null; 

   3: }

   4: using (Ajax.BeginForm("Find", new AjaxOptions { OnSuccess = "renderCustomerList" }))

   5: { 

   6:    <fieldset>

   7:        <legend>Maintain Contacts</legend>

   8:        <label class="control-label" for="firstName">First Name. :</label>

   9:        <input type="text" name="firstName" class="input-medium search-query" />

  10:        <label class="control-label" for="lastName">Last Name :</label>

  11:        <input type="text" name="lastName" class="input-medium search-query" />

  12:        <input type="submit" value="Retrieve" class="btn btn-primary" />

  13:    </fieldset>

  14: }

三、显示联系人列表

QueryFormPartial.cshtml定义了一个以Ajax方式提交的表单,目标Action为具有如下定义的Find,它根据指定的First Name和Last Name筛选匹配的联系人列表,并将其呈现在一个名为ContactListPartial的View中。

   1: public class HomeController : Controller

   2: {

   3:     //其他成员

   4:     public ActionResult Find(string firstName = "", string lastName = "")

   5:     { 

   6:         var result = from contact in contacts

   7:                         where (string.IsNullOrEmpty(firstName) || contact.FirstName.ToLower().Contains(firstName.ToLower()))

   8:                             &&(string.IsNullOrEmpty(lastName) || contact.LastName.ToLower().Contains(lastName.ToLower()))

   9:                         orderby contact.Id

  10:                         select contact;

  11:         return View("ContactListPartial",result.ToArray());

  12:     }

  13: }

如下所示的ContactListPartial.cshtml的定义,这是一个Model类型为IEnumerable<Contact>的强类型View,它以表格的形式将联系人列表呈现出来。

   1: @model IEnumerable<Contact>

   2: @{

   3:     Layout = null;    

   4: }

   5: <table class="table table-striped table-bordered">

   6:     <thead>    

   7:         <tr>

   8:             <th>ID</th>

   9:             <th>First Name</th>

  10:             <th>Last Name</th>

  11:             <th>Email Address</th>

  12:             <th>Phone No.</th>

  13:         </tr>        

  14:     </thead>

  15:     <tbody>

  16:         @foreach (var contact in Model)

  17:         { 

  18:             <tr>

  19:                 <td>@Ajax.ActionLink(contact.Id, "Update", new { contactId = contact.Id }, new AjaxOptions { OnSuccess = "showDialog" , HttpMethod="GET"})</td>

  20:                 <td>@contact.FirstName</td>

  21:                 <td>@contact.LastName</td>

  22:                 <td>@contact.EmailAddress</td>

  23:                 <td>@contact.PhoneNo</td>               

  24:             </tr>

  25:         }

  26:     </tbody>

  27: </table>

从QueryFormPartial.cshtml的定义可以看到,表单成功提交之后会调用一个名为renderCustomerList的JavaScript函数(@using (Ajax.BeginForm("Find", new AjaxOptions { OnSuccess = "renderCustomerList" }))),它以如下的方式定义在Index.cshtml中。从定义了看出,它将获取的数据(实际上ContactListPartial这个View最终的HTML)作为contactList这个<div>的HTML。

   1: <html>

   2:     <head>

   3:         <script type="text/javascript">
   1:  

   2:             function renderCustomerList(data) {

   3:                 $("#contactList").html(data);

   4:             }

   5:         

</script>

   4:     </head>

   5: </html>

四、弹出“修改联系人”对话框

从ContactListPartial.cshtml的定义可以看到联系人ID以一个链接的方式呈现出来,点击该链接会以Ajax的方式访问Action方法Update,当前联系人ID会作为请求的参数(<td>@Ajax.ActionLink(contact.Id, "Update", new { contactId = contact.Id }, new AjaxOptions { OnSuccess = "showDialog" , HttpMethod="GET"})</td>)。如下所示的是Action方法Update的定义,它根据指定的ID获取对应的联系人,并将其呈现在一个名为ContactPartial 的View中。

   1: public class HomeController : Controller

   2: {

   3:      //其他成员

   4:     [HttpGet]

   5:     public ActionResult Update(string contactId)

   6:     {

   7:         Contact contact = contacts.First(c => c.Id == contactId);

   8:         return View("ContactPartial", contact);

   9:     }

  10: }

如下所示的ContactPartial.cshtml的定义,这是一个Model类型为Contact的强类型View,它将联系人信息呈现在一个表单中。

   1: @model Contact

   2: @{

   3:     Layout = null;

   4: }

   5: @using(Ajax.BeginForm("Update", null, new AjaxOptions { HttpMethod = "Post", OnSuccess = "reLoad" }, new { @class = "form-horizontal" }))

   6: {

   7:     <div class="modal-header">     

   8:     <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>

   9:     <h3>Contact Information</h3>

  10:     </div>

  11:     <div class="modal-body">

  12:         <div class="control-group">

  13:             @Html.HiddenFor(model=>model.Id)

  14:             @Html.LabelFor(model=>model.FirstName,new{@class="control-label"})

  15:             <div class="controls">

  16:                 @Html.EditorFor(model => model.FirstName)

  17:                 @Html.ValidationMessageFor(model => model.FirstName)

  18:             </div>

  19:         </div>

  20:         <div class="control-group">

  21:             @Html.LabelFor(model=>model.LastName,new{@class="control-label"})

  22:             <div class="controls">

  23:                 @Html.EditorFor(model => model.LastName)

  24:                 @Html.ValidationMessageFor(model => model.LastName)

  25:             </div>

  26:         </div>

  27:         <div class="control-group">

  28:             @Html.LabelFor(model => model.EmailAddress, new { @class = "control-label" })

  29:             <div class="controls">

  30:                 @Html.EditorFor(model => model.EmailAddress)                

  31:                 @Html.ValidationMessageFor(model => model.EmailAddress)

  32:             </div>

  33:         </div>

  34:         <div class="control-group">

  35:             @Html.LabelFor(model => model.PhoneNo, new { @class = "control-label" })

  36:             <div class="controls">

  37:                 @Html.EditorFor(model => model.PhoneNo)                

  38:                 @Html.ValidationMessageFor(model => model.PhoneNo)

  39:             </div>

  40:         </div>

  41:     </div>

  42:     <div class="modal-footer">

  43:     <a href="#" class="btn" data-dismiss="modal">Close</a>

  44:     <input type="submit" class="btn btn-primary" value="Save" />

  45:     </div>

  46: }

联系人编译窗口的弹出最终通过调用JavaScript函数showDialog实现(<td>@Ajax.ActionLink(contact.Id, "Update", new { contactId = contact.Id }, new AjaxOptions { OnSuccess = "showDialog" , HttpMethod="GET"})</td>),具体定义如下所示。它将获取到的数据(实际上是ContactPartial这个View最终的HTML)作为第三个<div>的HTML,并按照Bootstrap的方式以模态对话框的形式将其呈现出来。至于中间的两行代码,在于解决动态添加表单无法实施验证的问题。

   1: <html>

   2:     <head>

   3:         <script type="text/javascript">
   1:            

   2:             function showDialog(data) {

   3:                 $("#contactDialog").html(data);

   4:                 $("#contactDialog form")

   5:                     .removeData("validator")

   6:                     .removeData("unobtrusiveValidation");

   7:                 $.validator.unobtrusive.parse($("#contactDialog form"));

   8:                 $("#contactDialog").modal();

   9:             }            

  10:         

</script>

   4:     </head>

   5: </html>

五、联系人信息的最终修改

通过ContactPartial.cshtml的定义可以看出编辑联系人表单最终以POST的方式提交到HomeController的Action方法Update(@using(Ajax.BeginForm("Update", null, new AjaxOptions { HttpMethod = "Post", OnSuccess = "reLoad" }, new { @class = "form-horizontal" }))),该Action方法具有如下的定义。它在完成修改之后,返回字符串“OK”表明联系人修改成功。

   1: public class HomeController : Controller

   2: {

   3:     //其他成员

   4:     [HttpPost]

   5:     public string  Update(Contact contact)

   6:     {

   7:         contacts.Remove(contacts.First(c=>c.Id == contact.Id));

   8:         contacts.Add(contact);

   9:         return "OK";

  10:     }

  11: }

联系人修改表单提交后关闭当前窗口并加载新的数据通过具有如下定义JavaScript函数Reload实现(@using(Ajax.BeginForm("Update", null, new AjaxOptions { HttpMethod = "Post", OnSuccess = "reLoad" }, new { @class = "form-horizontal" }))),该函数依然定义在Index.cshtml中。

   1: <html>

   2:     <head>

   3:         <script type="text/javascript">
   1:  

   2:             function reLoad(data) {

   3:                 if (data == "OK") {

   4:                     $("#contactDialog").modal("hide");

   5:                     $(".form-search form").submit();

   6:                 }

   7:             }

   8:         

</script>

   4:     </head>

   5: </html>

[ASP.NET MVC] 利用动态注入HTML的方式来设计复杂页面的更多相关文章

  1. ASP.NET MVC利用PagedList分页(二)PagedList+Ajax+JsRender

    (原文) 昨天在ASP.NET MVC利用PagedList分页(一)的 最后一节提到,一个好的用户体验绝对不可能是点击下一页后刷新页面,所以今天来说说利用Ajax+PagedList实现无刷新(个人 ...

  2. 转:【译】Asp.net MVC 利用自定义RouteHandler来防止图片盗链

    [译]Asp.net MVC 利用自定义RouteHandler来防止图片盗链   你曾经注意过在你服务器请求日志中多了很多对图片资源的请求吗?这可能是有人在他们的网站中盗链了你的图片所致,这会占用你 ...

  3. ASP.NET MVC IOC依赖注入之Autofac系列(二)- WebForm当中应用

    上一章主要介绍了Autofac在MVC当中的具体应用,本章将继续简单的介绍下Autofac在普通的WebForm当中的使用. PS:目前本人还不知道WebForm页面的构造函数要如何注入,以下在Web ...

  4. ASP.NET MVC中的Session以及处理方式

    最近在ASP.NET MVC项目中碰到这样的情况:在一个controller中设置了Session,但在另一个controller的构造函数中无法获取该Session,会报"System.N ...

  5. ASP.NET MVC 利用IRouteHandler, IHttpHandler实现图片防盗链

    你曾经注意过在你服务器请求日志中多了很多对图片资源的请求吗?这可能是有人在他们的网站中盗链了你的图片所致,这会占用你的服务器带宽.下面这种方法可以告诉你如何在ASP.NET MVC中实现一个自定义Ro ...

  6. [ASP.NET MVC] 利用自定义的AuthenticationFilter实现Basic认证

    很多情况下目标Action方法都要求在一个安全上下文中被执行,这里所谓的安全上下文主要指的是当前请求者是一个经过授权的用户.授权的本质就是让用户在他许可的权限范围内做他能够做的事情,授权的前提是请求者 ...

  7. [转]Asp.net MVC 利用PartialView 构造自定义菜单

    本文转自:http://www.cnblogs.com/huyq2002/archive/2012/01/06/2314838.html 在VS2010中利用Asp.net MVC自带的模板生成的菜单 ...

  8. asp.net mvc 利用过滤器进行网站Meta设置

    过去几年都是用asp.net webform进行开发东西,最近听说过时了,同时webform会产生ViewState(虽然我已经不用ruanat=server的控件好久了 :)),对企业应用无所谓,但 ...

  9. ASP.NET MVC使用动态产生meta

    在ASP.NET中,我们是很容易动态为header节点添加meta信息.<动态修改网页Header属性,Title,Meta标签等>http://www.cnblogs.com/insus ...

随机推荐

  1. Session 入库

    session入库 session机制中的数据分部分存储,1部分在客户端的cookie中,2部分在服务器端的session文件中. 务器端的session文件中存储的是$_SESSION变量中的数据. ...

  2. 仿《雷霆战机》飞行射击手游开发--GameObject

    转载请注明:http://www.cnblogs.com/thorqq/p/5646509.html 在上一篇中,我们介绍了各种游戏对象的功能及类的集成关系,现在我们来看看GameObject的源代码 ...

  3. 引用、return

    C语言中没有引用,引用(reference)是c++对c语言的重要扩充.通俗点说,引用就是“起别名”.比如变量data,和它的引用 RefData.虽然名字不同,但是操作他们的时候,都操作的是相同的内 ...

  4. HTML5之广播聊天室

    - 服务器端广播文本- 所有客户端都可以收到 --- 客户端 - 定义文本框- 定义发送事件 textarea accesskey =t oninput="sendmsg();"- ...

  5. 实现cookie跨域访问

    需求:A系统(www.a.com)里设置一个浏览器cookie,B系统(www.b.com)需要能够访问到A设置的cookie. 通过HTML SCRIPT标签跨域写cookie: 由于html的sc ...

  6. C# 与C++的数据转换

    一.类型转化 下面重点罗列下常用的类型转化. C++类型 C#类型 备注说明 Int Int16.Int32 没有悬念,直接转化 Uint UInt16.Uint32.int 在程序中,不太清楚是,就 ...

  7. mysql_sql语句之美

    无线地址及数量统计 单个用户无线登录信息统计

  8. (转)Qt Model/View 学习笔记 (二)——Qt Model/View模式举例

    Qt Model/View模式举例 Qt提供了两个标准的models:QStandardItemModel和QDirModel.QStandardItemModel是一个多用途的model,可用于表示 ...

  9. 数据结构二叉树的递归与非递归遍历之java,javascript,php实现可编译(1)java

    前一段时间,学习数据结构的各种算法,概念不难理解,只是被C++的指针给弄的犯糊涂,于是用java,web,javascript,分别去实现数据结构的各种算法. 二叉树的遍历,本分享只是以二叉树中的先序 ...

  10. Oracle回收站

    回收站是删除对象使用的存储空间.可以使用实例参数recyclebin禁用回收站,默认是on,可以为某个会话或系统设置为off或on.所有模式都有一个回收站. 当表空间不足时可以自动重用回收站对象占用的 ...