相比輔助器方法,模板輔助器方法更智能一些,它們可以指定想要顯示的屬性,而讓MVC框架去判斷應該使用什麼樣的HTML元素。只是,需要一些初期關註才能建立起來,但畢竟是一種顯示數據的更為靈活的方式。 這裡打算繼續使用介紹輔助器方法時使用的項目,但是,CreatePerson.cshtml視圖在之前的輔助 ...
相比輔助器方法,模板輔助器方法更智能一些,它們可以指定想要顯示的屬性,而讓MVC框架去判斷應該使用什麼樣的HTML元素。只是,需要一些初期關註才能建立起來,但畢竟是一種顯示數據的更為靈活的方式。
這裡打算繼續使用介紹輔助器方法時使用的項目,但是,CreatePerson.cshtml視圖在之前的輔助器方法會在生成的HTML元素上添加data屬性,來支持表單驗證,這一點在後面對模板輔助器方法的使用時打算禁用,但是,客戶端驗證特性對程式的其他部分仍然有效,調整後的代碼如下(粗體部分為修改的內容):
@model HelperMethods.Models.Person @{ ViewBag.Title = "CreatePerson"; Html.EnableClientValidation(false); } <h2>CreatePerson</h2> @using (Html.BeginRouteForm("FormRoute", new { }, FormMethod.Post, new { @class = "personClass", data_formType = "person" })) { <div class="dataElem"> <label>PersonId</label> @Html.TextBoxFor(m => m.PersonId) </div> <div class="dataElem"> <label>First Name</label> @Html.TextBoxFor(m => m.FirstName) </div> <div class="dataElem"> <label>Last Name</label> @Html.TextBoxFor(m => m.LastName) </div> <div class="dataElem"> <label>Role</label> @Html.DropDownListFor(m => m.Role, new SelectList(Enum.GetNames(typeof(HelperMethods.Models.Role)))) </div> <input type="submit" value="提交" /> }
使用模板輔助器方法
首先來看看編輯元素的輔助器方法:Html.EditorFor和Html.Editor。Editor方法的字元串參數是用來指定編輯器元素所需的屬性的。EditorFor是強類型的輔助器方法,可以使用lambda表達式指定編輯器元素所需要的模型屬性。為了演示,下麵同時混合使用了這兩種方法,實際項目中可以根據自己的喜好使用,但是還是推薦使用EditorFor方法,這樣可以避免由誤輸屬性名造成的錯誤:
@model HelperMethods.Models.Person @{ ViewBag.Title = "CreatePerson"; Html.EnableClientValidation(false); } <h2>CreatePerson</h2> <!--使用模板輔助器方法--> @using (Html.BeginRouteForm("FormRoute", new { }, FormMethod.Post, new { @class = "personClass", data_formType = "person" })) { <div class="dataElem"> <label>PersonId</label> @Html.Editor("PersonId") </div> <div class="dataElem"> <label>First Name</label> @Html.Editor("FirstName") </div> <div class="dataElem"> <label>Last Name</label> @Html.EditorFor(m => m.LastName) </div> <div class="dataElem"> <label>Role</label> @Html.EditorFor(m => m.Role) </div> <div class="dataElem"> <label>Birth Date</label> @Html.EditorFor(m => m.BirthDate) </div> <input type="submit" value="提交" /> }
最終效果如圖,這和之前的輔助器方法實現的一樣,只是增加了一個BirthDate的顯示:
這裡的日期顯示成這個樣子,是因為我的瀏覽器的問題,預設情況下IE瀏覽器對日期的顯示不怎麼友好,如果換成其他瀏覽器(如Opera瀏覽器)就可以正常顯示,當然在IE下可以通過jQuery等一些第三方的控制項實現。所以在開發Web端程式的時候一定要註意不同瀏覽器直接的差異(對於這幾個屬性的顯示不同的還有PersonId等)。
HTML5規範對input元素所編輯的常規的屬性類型,定義了一些不同的類型,如數字和日期,而這裡的模板輔助器方法採用的就是HTML5的新類型。下麵是我的瀏覽其中生成的HTML結果:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>CreatePerson</title> <link href="/Content/Site.css" rel="stylesheet"> <style type="text/css"> label { display: inline-block; width: 100px; } .dateElem { margin: 5px; } </style> </head> <body> <h2>CreatePerson</h2> <!--使用模板輔助器方法--> <form class="personClass" action="/app/forms/Home/CreatePerson" method="post" data-formtype="person"> <div class="dataElem"> <label>PersonId</label> <input name="PersonId" class="text-box single-line" id="PersonId" type="number" value="0"> </div> <div class="dataElem"> <label>First Name</label> <input name="FirstName" class="text-box single-line" id="FirstName" type="text" value=""> </div> <div class="dataElem"> <label>Last Name</label> <input name="LastName" class="text-box single-line" id="LastName" type="text" value=""> </div> <div class="dataElem"> <label>Role</label> <input name="Role" class="text-box single-line" id="Role" type="text" value="Admin"> </div> <div class="dataElem"> <label>Birth Date</label> <input name="BirthDate" class="text-box single-line" id="BirthDate" type="datetime" value="0001/1/1 0:00:00"> </div> <input type="submit" value="提交"> </form> </body> </html>
Type標簽屬性指定了input元素應當顯示的類型,但是,可惜不是所有的瀏覽器都能支持,原因就是HTML5特性比較新。
後面會展示如何為MVC框架提供附加信息,以便改善輔助器方法生成的HTML,下麵先看看完整的輔助器集。
輔助器 |
示例 |
描述 |
Display |
Html.Display ("FirstName") |
渲染指定模型屬性的只讀視圖,會根據該屬性的類型及元數據選用一個HTML元素 |
DisplayFor |
Html.DisplayFor(x => x.FirstName) |
上一輔助器的強類型版本 |
Editor |
Html.Editor("FirstName") |
渲染指定模型屬性的一個編輯器,會根據該屬性的類型及元數據選用一個HTML元素 |
EditorFor |
Html.EditorFor(x => x.FirstName) |
上一輔助器的強類型版本 |
Label |
Html.Label("FirstName") |
渲染指定模型的HTML<label>元素(註意:它顯示的是指定屬性的屬性名,而非屬性值) |
LabelFor |
Html.LabelFor(x => x.FirstName) |
上一輔助器的強類型版本 |
生成標簽和顯示元素
後面對Home控制器做一下修改,用來演示這些輔助器方法:
using HelperMethods.Models; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace HelperMethods.Controllers { public class HomeController : Controller { public ActionResult Index() { ViewBag.Fruits = new string[] { "Apple", "Orange", "Pear" }; ViewBag.Cities = new string[] { "New York", "London", "Paris" }; string message = "This is an HTML element: <input>"; return View((object)message); } public ActionResult CreatePerson() { return View(new Person()); } [HttpPost] public ActionResult CreatePerson(Person person) { return View("DisplayPerson", person); } } }
上面的DisplayPerson對應的視圖文件添加到/Views/Home文件夾中,其內容如下:
@model HelperMethods.Models.Person @{ ViewBag.Title = "DisplayPerson"; } <h2>DisplayPerson</h2> <div class="dataElem"> @Html.Label("PersonId") @Html.Display("PersonId") </div> <div class="dataElem"> @Html.Label("FirstName") @Html.Display("FirstName") </div> <div class="dataElem"> @Html.LabelFor(m => m.LastName) @Html.DisplayFor(m => m.LastName) </div> <div class="dataElem"> @Html.LabelFor(m => m.Role) @Html.DisplayFor(m => m.Role) </div> <div class="dataElem"> @Html.LabelFor(m => m.BirthDate) @Html.DisplayFor(m => m.BirthDate) </div>
啟動程式,導航至/Home/CreatePerson後,填充內容,並點擊“提交”按鈕,便可看到下圖的結果:
下麵是這些輔助器方法生成的HTML,需要註意的是,Display和DisplayFor方法預設情況下並未生成HTML元素——它們只是發佈了它們所操作的屬性值:
<!DOCTYPE html> <html><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>DisplayPerson</title> <link href="/Content/Site.css" rel="stylesheet"> <style type="text/css"> label { display: inline-block; width: 100px; } .dateElem { margin: 5px; } </style> </head> <body> <h2>DisplayPerson</h2> <div class="dataElem"> <label for="PersonId">PersonId</label> 100 </div> <div class="dataElem"> <label for="FirstName">FirstName</label> Adam </div> <div class="dataElem"> <label for="LastName">LastName</label> Freeman </div> <div class="dataElem"> <label for="Role">Role</label> Admin </div> <div class="dataElem"> <label for="BirthDate">BirthDate</label> 0001/1/1 0:00:00 </div> </body></html>
看似這些輔助器不是很有用,但可以做些調整來使其產生更希望顯示給用戶的輸出結果。
使用整體模型模板輔助器
前面說的都是針對單個屬性的輔助器,其實還有一些針對整個對象的輔助器——這稱之為支架輔助器。
輔助器 |
示例 |
描述 |
DisplayForModel |
Html.DisplayForModel() |
渲染整個模型對象的只讀視圖 |
EditorForModel |
Html.EditorForModel() |
渲染整個模型對象的編輯器元素 |
LabelForModel |
Html.LabelForModel() |
渲染對整個模型對象進行引用的HTML<label>元素 |
註:所謂模板輔助器(Templated Helper)是在視圖中使用的一種輔助性工具,用以為視圖模型的屬性生成HTML標記。模板輔助器有兩大類:一類就叫模板輔助器(Templated Helper),作用是在視圖中為模型的個別屬性生成HTML標記。另一類叫作支架輔助器(Scaffolding Helper),作用是在視圖中創建整個模型所有屬性的HTML標記。這兩種模板輔助器又分別有三種:
- 標簽輔助器(Label、LabelFor、LabelForModel),用於生成模型屬性的標簽(以屬性名稱作為標簽);
- 顯示輔助器(Display、DisplayFor、DisplayForModel),用於生成模型屬性的顯示標記(顯示屬性的值,或顯示只讀數據);
- 編輯輔助器(Edit、EditFor、EditForModel),用於生成對模型屬性進行編輯的標記(編輯器)。
下麵演示一下通過LabelForModel和EditForModel 輔助器簡化CreatePerson.cshtml視圖的結果:
@model HelperMethods.Models.Person @{ ViewBag.Title = "CreatePerson"; Html.EnableClientValidation(false); } <h2>CreatePerson: @Html.LabelForModel()</h2> @using (Html.BeginRouteForm("FormRoute", new { }, FormMethod.Post, new { @class = "personClass", data_formType = "person" })) { @Html.EditorForModel() <input type="submit" value="提交" /> }
下麵是效果,雖然還有些不太正確,主要是因為支架輔助器生成的HTML與前面的佈局視圖中CSS的定義不對應,以及它會預設顯示所有的模型中的屬性,後面做一些修改便可達到我們的目標:
下麵先通過對樣式作簡單的改動,對視圖外觀加以整理,以使它們預製件輔助器添加到div和input元素中的CSS的class值對應起來。文件_Layout.cshtml修改的結果:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <link href="~/Content/Site.css" rel="stylesheet" /> <style type="text/css"> label { display: inline-block; width: 100px; } .dateElem { margin: 5px; } h2 > label { width: inherit; } .editor-label, .editor-field { float: left; } .editor-label, .editor-label label, .editor-field input { height: 20px; } .editor-label { clear: left; } .editor-field { margin-left: 10px; margin-top: 10px; } input[type=submit] { float: left; clear: both; margin-top: 10px; } .column { float: left; margin: 10px; } </style> </head> <body> @RenderBody() </body> </html>
使用模型元數據
用前面的輔助器模板生成的HTML可以看出其並非十分智能,經常會產生不需要的HTML。但是,我們可以通過模型元數據(MetaData)加以輔助,從而改善這種結果。它的實現原理是:使用C#註解屬性,以及註解屬性的參數值來為輔助器提供一定的指示,輔助器方法在生成HTML元素時將會根據這些信息進行組織。
用元數據控制編輯及可見性
比如我們通過HiddenInput註解屬性將Person類中的PersonId屬性隱藏後,在運用這個註解屬性時,Html.EditorFor和Html.EditorForModel輔助器會生成一個對應的只讀視圖,下麵分別是代碼示例和效果圖:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace HelperMethods.Models { public class Person { [HiddenInput] public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime BirthDate { get; set; } public Address HomeAddress { get; set; } public bool IsApproved { get; set; } public Role Role { get; set; } } public class Address { public string Line1 { get; set; } public string Line2 { get; set; } public string City { get; set; } public string PostalCode { get; set; } public string Country { get; set; } } public enum Role { Admin, User, Guest } }
這樣得到了一個該屬性值的文本顯示效果,且在HTML中生成了一個字元串值和一個隱藏的input元素。如果不希望顯示該屬性值,可以將該註解屬性的DisplayValue參數設置為false,如:
… public class Person { [HiddenInput(DisplayValue = false)] public int PersonId { get; set; } … } …
此時,即便使用的是類似Html.EditorFor這樣的針對某一屬性的模板輔助器,任會達到上面的效果。
排除支架中的一個屬性
如果不需要某一屬性生成HTML元素,可以使用ScaffoldColumn(命名空間為:System.ComponentModel.DataAnnotations)註解屬性。相對於HiddenInput,ScaffoldColumn是將某一屬性標記為完全禁止,即在生成HTML的時候將不會將該屬性生成對應的HTML元素,如下麵示例:
[ScaffoldColumn(false)]
public int PersonId { get; set; }
但是,這麼做也有一個問題,就是這對模型綁定是有影響的,而且對模板輔助器(相對於支架輔助器的那種)是不起作用的。
使用用於標簽的元數據
預設情況下,Label、LabelFor、LabelForModel,以及EditorForMordel輔助器會以屬性名稱作為它們生成的標簽元素的內容,但實際項目中經常不希望這麼做,尤其是在中文環境的項目中。這個時候可以使用System.ComponentModel命名空間下的DisplayName註解屬性,併為其參數提供希望的值即可(同時還有System.ComponentModel.DataAnnotations中的Display註解屬性)。如:
using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using Syste