Asp.Net MVC4 系列--進階篇之Helper(2)

mybwu_com發表於2014-04-13

本章接著介紹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成功的工作了。

相關文章