閱讀536 返回首頁    go 阿裏雲 go 技術社區[雲棲]


[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的鏈接後,會以“模態對話框”的形式顯示當前聯係人的編輯“窗口”(右圖)。

20121123191742429.png
201211231917461382.png
20121123191747924.png

這個“單頁麵應用”是通過ASP.NET MVC開發的,接下來我們來逐步介紹如果將同一頁麵中的這三塊不同的內容提取出來進行“分而治之”。

如下所示的是表示聯係人的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, "", 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{@})
  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{@})
  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, "", new { contactId = contact.Id }, new AjaxOptions { OnSuccess = "" , 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("", null, new AjaxOptions { HttpMethod = "", 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("", null, new AjaxOptions { HttpMethod = "", OnSuccess = "" }, 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>

作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-25 16:04:17

  上一篇:go  Aliware推出應用配置管理大殺器,分布式架構下配置推送秒級生效!
  下一篇:go  如何解決jQuery Validation針對動態添加的表單無法工作的問題?