[ASP.NET MVC] 利用动态注入HTML的方式来设计复杂页面
原文:[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">×</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的方式来设计复杂页面的更多相关文章
- ASP.NET MVC利用PagedList分页(二)PagedList+Ajax+JsRender
(原文) 昨天在ASP.NET MVC利用PagedList分页(一)的 最后一节提到,一个好的用户体验绝对不可能是点击下一页后刷新页面,所以今天来说说利用Ajax+PagedList实现无刷新(个人 ...
- 转:【译】Asp.net MVC 利用自定义RouteHandler来防止图片盗链
[译]Asp.net MVC 利用自定义RouteHandler来防止图片盗链 你曾经注意过在你服务器请求日志中多了很多对图片资源的请求吗?这可能是有人在他们的网站中盗链了你的图片所致,这会占用你 ...
- ASP.NET MVC IOC依赖注入之Autofac系列(二)- WebForm当中应用
上一章主要介绍了Autofac在MVC当中的具体应用,本章将继续简单的介绍下Autofac在普通的WebForm当中的使用. PS:目前本人还不知道WebForm页面的构造函数要如何注入,以下在Web ...
- ASP.NET MVC中的Session以及处理方式
最近在ASP.NET MVC项目中碰到这样的情况:在一个controller中设置了Session,但在另一个controller的构造函数中无法获取该Session,会报"System.N ...
- ASP.NET MVC 利用IRouteHandler, IHttpHandler实现图片防盗链
你曾经注意过在你服务器请求日志中多了很多对图片资源的请求吗?这可能是有人在他们的网站中盗链了你的图片所致,这会占用你的服务器带宽.下面这种方法可以告诉你如何在ASP.NET MVC中实现一个自定义Ro ...
- [ASP.NET MVC] 利用自定义的AuthenticationFilter实现Basic认证
很多情况下目标Action方法都要求在一个安全上下文中被执行,这里所谓的安全上下文主要指的是当前请求者是一个经过授权的用户.授权的本质就是让用户在他许可的权限范围内做他能够做的事情,授权的前提是请求者 ...
- [转]Asp.net MVC 利用PartialView 构造自定义菜单
本文转自:http://www.cnblogs.com/huyq2002/archive/2012/01/06/2314838.html 在VS2010中利用Asp.net MVC自带的模板生成的菜单 ...
- asp.net mvc 利用过滤器进行网站Meta设置
过去几年都是用asp.net webform进行开发东西,最近听说过时了,同时webform会产生ViewState(虽然我已经不用ruanat=server的控件好久了 :)),对企业应用无所谓,但 ...
- ASP.NET MVC使用动态产生meta
在ASP.NET中,我们是很容易动态为header节点添加meta信息.<动态修改网页Header属性,Title,Meta标签等>http://www.cnblogs.com/insus ...
随机推荐
- 第二节 hibernate session介绍以及session常用方法介绍
原创地址:http://www.cnblogs.com/binyulan/p/5628579.html Session是java应用程序和hibernate框架之间的一个主要接口.它是从持久化服务中剥 ...
- WIN32一些文件及窗口操作
0,获取指定的系统路径: #include <shlobj.h> #pragma comment(lib, "shell32.lib") TCHAR szPat ...
- 在Java中弹出位于其他类的由WindowsBuilder创建的JFrameApplicationWIndow
我们一般在使用Java弹出窗体的时候,一般是使用Jdialog这个所谓的"对话框类".但是,如果你不是初学者或研究员,而会在使用Java进行swing项目的开发,那么你很可能用到一 ...
- open_clientfd(char* hostname,int port)和open_listenfd(int port)
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h&g ...
- 印象笔记无法同步问题解决 Unable to send HTTP request: 12029
问题 今天突然发现本地软件不能访问网络. 包括: 印象笔记无法同步, 搜狗输入法无法登陆. 但其它上网正常. 思路及解决过程 因为chrome上网 ,qq上网均正常. 且同事可以正常使用. 推测是本地 ...
- PHP 魔术方法 __clone __toString(五)
__clone() - 当对象克隆的时候自动加载此方法 __toString() - 当对象需要echo打印输出的时候自动加载此方法 __clone() <?php class example{ ...
- PHP 魔术方法 __isset __unset (三)
慢慢长寻夜,明月高空挂 __isset() - 在对类中属性或者非类中属性使用isset()方法的时候如果没有或者非公有属性,则自动执行__isset()的方法 __unset() - 在对类中属性 ...
- border-radius导致overflow:hidden失效问题。
如果一个父元素设置了overflow:hidden属于的同时还设置了border-radius属性,那么如果想隐藏超出的子元素,四个圆角处会出现超出圆角依然显示的bug: 一种方法是运用-webkit ...
- jquery + json 操作
jquery 读取集合对象多是要与json进行解析操作的,以下自己经过多方资料查找,终于有一套自己的方式组合. 1.首先创建web services或一般处理程序,用于显示获取Datatable对象 ...
- YII框架源码分析(百度PHP大牛创作-原版-无广告无水印)
YII 框架源码分析 百度联盟事业部——黄银锋 目 录 1. 引言 3 1.1.Yii 简介 3 1.2.本文内容与结构 3 2.组件化与模块化 4 2.1.框架加载和运行流程 4 ...