本章接着介绍Asp.NetMVC4中的Helper
首先做准备工作,为了读者方便阅读,笔者把上篇文章中(Asp.Net MVC4系列—进阶篇之Helper(1)) 的代码再复制在这边一份,这篇文章都以此为开始:
Person类(Model中):
public class Person { 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 }
PersonController.cs
public class PersonController : Controller { public ActionResult CreatePerson() { return View(new Person()); } [HttpPost] public ActionResult CreatePerson(Person person) { return View(person); } }
Route:
routes.MapRoute( name: "FormRoute", url:"app/forms/{controller}/{action}" ); }
View(CreatePerson.cshtml)
<html> @modelMVCHelperStudy.Models.Person @{ ViewBag.Title = "CreatePerson"; } <h2>CreatePerson</h2> <body> @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>FirstName</label> @Html.TextBoxFor(m => m.FirstName) </div> <div class="dataElem"> <label>LastName</label> @Html.TextBoxFor(m =>m.LastName) </div> <div class="dataElem"> <label>Role</label> @Html.DropDownListFor(m =>m.Role, new SelectList(Enum.GetNames(typeof(MVCHelperStudy.Models.Role)))) </div> <input type="submit" value="Submit" /> } </body> </html>
测试运行:
首先,使用模板方法重构View
把表单中间代码替换为:
<div class="dataElem"> <label>PersonId</label> @Html.Editor("PersonId") </div> <div class="dataElem"> <label>FirstName</label> @Html.Editor("FirstName") </div> <div class="dataElem"> <label>LastName</label> @Html.EditorFor(m =>m.LastName) </div> <div class="dataElem"> <label>Role</label> @Html.EditorFor(m => m.Role) </div> <div class="dataElem"> <label>BirthDate</label> @Html.EditorFor(m =>m.BirthDate) </div>
代码说明:使用了Html.Editor替代以前的实现。
看一下生成的html
<form action="/app/forms/Person/CreatePerson" class="personClass" data-formType="person" method="post"><div class="dataElem"> <label>PersonId</label> <input class="text-box single-line" data-val="true" data-val-number="The field PersonId must be anumber." data-val-required="The PersonId field is required." id="PersonId" name="PersonId" type="number" value="0" /> </div> <div class="dataElem"> <label>FirstName</label> <input class="text-box single-line" id="FirstName" name="FirstName" type="text" value="" /> </div> <div class="dataElem"> <label>LastName</label> <input class="text-boxsingle-line" id="LastName" name="LastName" type="text" value="" /> </div> <div class="dataElem"> <label>Role</label> <input class="text-boxsingle-line" data-val="true" data-val-required="The Rolefield is required." id="Role" name="Role" type="text" value="Admin" /> </div> <div class="dataElem"> <label>BirthDate</label> <input class="text-boxsingle-line" data-val="true" data-val-date="The fieldBirthDate must be a date." data-val-required="The BirthDate field isrequired." id="BirthDate" name="BirthDate" type="datetime" value="1/1/0001 12:00:00 AM" /> </div> <input type="submit" value="Submit" /> </form>
对比之前生成的html,区别就是多了“type”属性,好处是它使得html5根据不同的type显示不同,这需要支持html5的浏览器来实现。
常用的MVC模板helper方法
Html.Display(“name”) |
Html.DisplayFor(m=>m.name) |
Html.Editor(“name”) |
Html.EditorFor(m=>m.name) |
Html.Label(“name”) |
Html.LabelFor(m=>m.name) |
Label 方法和Display方法
调整Controller代码:
public ActionResult CreatePerson() { return View(newPerson() { FirstName = "iori", LastName = "l", PersonId = 100, BirthDate = DateTime.Now.AddYears(-20) }); }
返回一个默认的Person。
调整View代码:
<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>
为了对比Display和Label,因此分别显示。测试结果:
可以看到,Display是显示Property的Value,而Label是显示Property的Name
再对比一下生成的html:
<form action="/app/forms/Person/CreatePerson" class="personClass" data-formType="person" method="post"> <div class="dataElem"> <label for="PersonId">PersonId</label> 100 </div> <div class="dataElem"> <label for="FirstName">FirstName</label> iori </div> <div class="dataElem"> <label for="LastName">LastName</label> l </div> <div class="dataElem"> <label for="Role">Role</label> Admin </div> <div class="dataElem"> <label for="BirthDate">BirthDate</label> 13/4/1994 9:24:09 PM </div> <input type="submit" value="Submit"/> </form>
可以看到,label自动生成的for属性,而display则直接把值打在了页面上,甚至没有conver任何标签。
Whole-Model Template
Whole-Model Template 主要包括:
Html.DisplayForModel() |
根据当前model每个property的类型,生成相应html(只读) |
Html.EditForModel() |
根据当前model的每个property的类型,生成相应可编辑的html |
Html.LabelForModel() |
根据当前的model生成标签 |
使用Html.EditForModel重构View代码:
@using(Html.BeginRouteForm("FormRoute", new {},FormMethod.Post, new { @class = "personClass",data_formType="person"})) { @Html.EditorForModel() <input type="submit" value="Submit" /> }
运行,查看结果
使用model metadata
1. hiddenInput
给PersonId加一个属性:
[HiddenInput(DisplayValue=false)]
运行,测试:
点击submit
Debug查看Person的值,可以看到PersonId被提交了,值为100。原因是我们的attribute告诉mvcframework把这个input解析为Hidden,查看html:
<input data-val="true" data-val-number="The field PersonId must be a number. " data-val-required="The PersonId field is required." id="PersonId" name="PersonId" type="hidden" value="100"/>
可以看到,type已经被解析为了hidden
使用[ScaffoldColumn]属性
一个类中不是每个字段都是需要mvc frame解析为html的,如果希望跳过某个property,可以使用这个属性。例如:
[ScaffoldColumn(false)] public int PersonId { get; set; }
这样的话,PersonId根本不会参与从property解析到html的过程。
使用[Display(Name=”XXX”)]
如果希望html解析出来的Label显示成别的字,可以使用这个属性。例如:
[Display(Name = "Birth Date")] public DateTime BirthDate { get; set; }
对应的html标签:
<label for="BirthDate">BirthDate</label>
使用 [DataType(type)]
如果想改变mvc frame 解析的类型,可以使用此属性,例如:
[DataType(DataType.Date)] public DateTime BirthDate { get; set; }
这样mvc解析这个property就会当做Date类型,而不是DateTime了。
相应的html标签:
<input class="text-boxsingle-line" data-val="true" data-val-date="The field BirthDate mustbe a date." data-val-required="The BirthDate field is required." id="BirthDate" name="BirthDate" type="date" value="13/4/1994" />
其他DataType可以选择的类型
DateTime |
Date |
Time |
Text |
PhoneNumber |
MultipleText |
Password |
Url |
EmailAddress |
使用[UIHint(“XX”)]属性
UIHint属性允许指定一个模板名称,mvc framework解析的时候,看到这个属性就会先找到对应的模板,再解析为相应的html,例如:
[UIHint("MultilineText")] public string FirstName { get; set; }
相应的html:
<textarea class="text-box multi-line" id="FirstName" name="FirstName"> iori</textarea>
MVC 自带的模板
Boolean |
DateTime |
Date |
EmailAddress |
HiddenInput |
Html |
MultiLineText |
Number |
Object |
Password |
String |
Time |
Text |
Tel |
Url |
Mvc会为每个模板生成对应的html,读者可以一一尝试,根据自己的需要选择的应用到项目中。每个模板,也是可以被重写的。
使用metadata类
1 . 修改Person类为partial,去掉所有的DataType,Hint(就像一个Entity类):
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; }
为了给Person类应用metadata 类型,需要先将其设为partial
2. 创建一个metadata 类
public partial class PersonMetaData { [HiddenInput(DisplayValue=false)] public int PersonId { get; set; } [Display(Name="First")] public string FirstName { get; set; } [Display(Name = "Last")] public string LastName { get; set; } }
3. 在Person类加上metadatatype属性
[MetadataType(typeof(PersonMetaData))]
4. View 中使用EditFormModel
@using(Html.BeginRouteForm("FormRoute",new {}, FormMethod.Post, new { @class = "personClass",data_formType="person"})) { @Html.EditorForModel() <input type="submit" value="Submit" /> }
使用metadata类的目的,希望Entity类和MVC Attribute标记过的类分开,但显示的时候,希望MVCFramework会从medadata类中找到一致的Property,拿出Attribute应用。运行,查看效果,
可以看到标签名字变了,PersonId也隐藏了,因此Metadata类的Display标签和Hidden标签都产生了效果。
解析嵌套类型 : Address
前面的例子,由于HomeAddress字段是class,因此MVCFramework没有识别出来,因此需要手动去再调用一下EditFor:
<p> @Html.EditorFor(m => m.HomeAddress) </p>
运行:
可以看到这样Address类型就可以被识别了。
显示指定Template
Html.EditorFor(m =>m.SomeProperty, "MyTemplate")
可以告诉MVC Framework,使用指定的Template。
Template的查找顺序
1. Customize的template
2. Built-in的template
3. 传入Helper的Template,e.g. : Html.EditorFor(m=>m.name,”T”);
4. UIHint
5. DataType
Customize一个Template
在Shared文件夹建一个EditorTemplates文件夹,因为所有的customize的template,MVCFramework都会来这里找。
创建一个View放在EditorTemplates文件夹,可以反射出枚举的每一个成员并显示:
@model Enum @Html.DropDownListFor(m => m,Enum.GetValues(Model.GetType()) .Cast<Enum>() .Select(m => { string enumVal =Enum.GetName(Model.GetType(), m); return new SelectListItem() { Selected = (Model.ToString() ==enumVal), Text = enumVal, Value = enumVal }; }))
应用,在metadata类给Role成员加UIHint
[UIHint("Enum")] public Role Role { get; set; }
运行,查看结果
可以看到,自定义的Template成功的工作了。