Asp.Net MVC4系列--進階篇之Helper(1)
從本章開始,將為讀者介紹MVC4中的helper使用方法
從sample開始
準備controller和Action
public class TestController : Controller
{
public ActionResult Index()
{
ViewBag.Fruits = new[] { "Apple", "Orange","Pear" };
ViewBag.Cities = new[] { "New York", "London","Paris" };
string message = "This is an HTML element: <input>";
return View("List",(object)message);
}
}
程式碼說明:準備了一個TestController,包含了一個Index Action(預設Action),Action中存了一些stringArray在ViewBag,返回了一個字串(包含了htmltag)。
準備View
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
Here are the fruits:
@foreach (string str in (string[])ViewBag.Fruits) {
<b>@str </b>
}
</div>
<div>
Here are the cities:
@foreach (string str in (string[])ViewBag.Cities) {
<b>@str </b>
}
</div>
<div>
Here is the message:
<p>@Model</p>
</div>
</body>
</html>
程式碼說明:把Viewbag中的兩個string Array列印出來,並列印出傳來的message。
測試:
可以看到,View列印出了string array,並且htmltag被識別為了string。
下一步,建立一個inline的helper
現在希望對列印字串陣列的View中的程式碼做一下封裝,這樣可以很好的控制列印的這部分實現。
於是給出實現:
@helper ListArrayItems(string[] items) {
foreach(string str in items) {
<b>@str </b>
}
}
在列印時候,就像呼叫C#函式那樣呼叫就可以了,傳入ViewBag中的Array:
<div>
Here are the fruits:
@ListArrayItems(ViewBag.Fruits)
</div>
<div>
Here are the cities:
@ListArrayItems(ViewBag.Cities)
</div>
使用helper的好處顯而易見,其實就是封裝帶來的好處,我們可以集中對行為進行控制,例如希望顯示為列表,只需要給helper改為:
@helper ListArrayItems(string[] items) {
<ul>
@foreach(string str in items) {
<li>@str</li>
}
</ul>
}
測試:
實現ExternalHelper method
不想把程式碼coding在View裡,希望寫在C#裡來管理,那麼可以考慮實現一個External版本的:
第一步,實現一個customizehelper
Public static class MyHelpers
{
public staticMvcHtmlString ListArrayItems(this HtmlHelper html, string[] list)
{
var tag =new TagBuilder("ul");
foreach(var str in list)
{
var itemTag = new TagBuilder("li");
itemTag.SetInnerText(str);
tag.InnerHtml += itemTag.ToString();
}
return new MvcHtmlString(tag.ToString());
}
}
可以看到,實際上是對HtmlHelper的擴充套件,接收一個stringarray引數,列印為<ul></li>列表,使用的是TagBuilder,返回值是MvcHtmlString。
1.在View中comment掉以前的inline版本,應用external版本的實現:
@*@helper ListArrayItems(string[] items) {
<ul>
@foreach(string str in items) {
<li>@str</li>
}
</ul>
} *@
@using MVCHelperStudy.CustomizeHelper
2.呼叫
<div>
Here are the fruits:
@Html.ListArrayItems((string[])ViewBag.Fruits)
</div>
<div>
Here are the cities:
@Html.ListArrayItems((string[])ViewBag.Cities)
</div>
HtmlHelper中常用的property:
RouteCollection |
Application定義的路由 |
ViewBag |
從呼叫的Controller/Action傳進來的ViewBag |
ViewContext |
包含了請求資訊,Controller,View等等 |
ViewContext中常用的property
Controller |
當前請求呼叫的Controller |
HelperContext |
包含了當前請求的詳細資訊(Request,Reponse等等) |
IsChildAction |
呼叫是否來自ChildAction |
RouteData |
當前請求的路由資訊 |
View |
呼叫當前helper的view |
TagBuilder的常用函式,property
InnerHtml |
設定控制元件內部html |
SetInnerText(string) |
設定控制元件包含的text |
AddCssClass(string) |
設定控制元件的css class |
MergeAttribute(string,string,bool) |
設定attribute,指定是否覆蓋舊值 |
Partial View , Child Action , Helper Method
前面章節講過了PartialView,ChildAction,這次也演示了Helper的用法,那麼什麼時候用哪個?
一般的,Helper會對Html的封裝多一些,如果希望封裝部分html的展示方法,可以考慮helper,一般會使用inline形式的,因為個人認為mvc後端寫external版本的helper比較蹩腳。
如果對於場景:來了一個請求,找到了action,action希望停留在當前view,完成一個區域性重新整理(ajaxcall),那麼可考慮partialview最合適了,這種場景,只涉及到action接到請求,來選擇view。
如果再複雜一些,view中一定要callaction,操作一下model,反過來render一個view,那麼就考慮採用childaction。不過一般情況下,接收請求返回partialview足以cover大部分需要應用ajax的場景,如果不夠,對於比較特殊的頁面,例如需要SPA(singlepage application ) , 那麼js template + JsonResult也是個不錯的方案。
字串Encoding
對於上例,Action中返回的字串中包含了”<input>”,如果希望mvc自動識別為一個輸入框,如何做?
準備一個helper
public static MvcHtmlStringDisplayMessage(this HtmlHelper html, string msg)
{
string result =String.Format("This is the message: <p>{0}</p>", msg);
return new MvcHtmlString(result);
}
與直接顯示出model(字串型別)不同,我們包了一層,構造一個MvcHtmlString物件丟給mvcframework,這樣它就會識別為一個html輸出到瀏覽器。
改一下view
把剛才的View程式碼刪了,新增以下程式碼:
@model string
@using MVCHelperStudy.CustomizeHelper
<html>
<head>
<meta name="viewport" content="width=device-width"/>
<title>Index</title>
</head>
<body>
<p>This is the contentfrom the view:</p>
<div style="border: thin solid black; padding: 10px">
Here is the message:
<p>@Model</p>
</div>
<p>This is the content from the helper method:</p>
<div style="border: thin solid black; padding: 10px">
@Html.DisplayMessage(Model)
</div>
程式碼說明:我們列印了兩個字串,一個是直接取@Model的,一個是呼叫了新的helper(Wrap為MvcHtmlString物件)
測試
可以看到我們的html字串被mvcframework識別為了控制元件render在瀏覽器上
現在改一下,如果我們的目的就是希望mvcframework把我的html字串識別為字串,那麼helper怎麼寫,簡單的返回字串就好了:
Public static string DisplayMessage(this HtmlHelper html, string msg) {
return String.Format("This is the message:<p>{0}</p>", msg);
}
第二種方法
使用Html.Encode,依然返回MvcHtmlString:
Public static MvcHtmlString DisplayEncodeMessage(this HtmlHelper html, string msg) {
String encodedMessage = html.Encode(msg);
string result = String.Format("This is the message: <p>{0}</p>", encodedMessage);
return new MvcHtmlString(result);
}
View呼叫一下:
Remove掉所有程式碼新增這行,@Html.DisplayEncodeMessage(@Model)
測試:
使用自帶的helper來構造一個表單
Mvc framework給我們提供了很多自帶的生成html的方法,讓我們可以生成表單。
從一個例子開始,
在Model資料夾新增Person類
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
}
一共三個類:person,address,role。
準備一個Controller,程式碼
public class PersonController : Controller
{
public ActionResult CreatePerson()
{
return View(new Person());
}
[HttpPost]
public ActionResult CreatePerson(Person person)
{
return View(person);
}
}
程式碼說明:
準備兩個action,一個是不帶資料的,主要針對第一次來到這個頁面的情況;一個是接收資料的,針對提交表單的情況,並使用了httppost對請求訪問進行限制。
View的實現
<html>
@model MVCHelperStudy.Models.Person
@{
ViewBag.Title = "CreatePerson";
}
<h2>CreatePerson</h2>
<form action="/Person/CreatePerson" method="post">
<div class="dataElem">
<label>PersonId</label>
<input name="personId" value="@Model.PersonId"/>
</div>
<div class="dataElem">
<label>First Name</label>
<input name="FirstName" value="@Model.FirstName"/>
</div>
<div class="dataElem">
<label>Last Name</label>
<input name="lastName" value="@Model.LastName"/>
</div>
<input type="submit" value="Submit" />
</form>
程式碼說明:
在不使用任何html helper 函式的情況下,手動coding一個form,表單指向Person/CreatePerson,method為post。
測試
1.第一次訪問這個頁面,可以看到View拿到了一個空的Person物件
2.Fill這個form:
3.提交表單
筆者在Action設定了斷點,為了說明請求成功被路由到了post版本的CreatePersonAction中。
注意,我們手動為每一個input設定了name屬性,這個很重要,因為mvcFramework在提交表單時會拿input的name屬性和model進行匹配並賦值,如果把name拿掉會怎樣?提交表單時這個input的值就沒法帶到action了。例如拿掉lastName的name屬性:
<input value="@Model.LastName"/>
然後按照剛才的流程提交表單,
在除錯狀態下檢視LastName的值:
可以看到,FirstName和Id的數值都被帶過來了,但是LastName由於name屬性沒有設定導致mvcframework沒有辦法匹配,因此資料沒有被正確賦值導致binding失敗,關於modelbinding,後面章節會講到。
使用Html.BeginForm, Html.EndForm 生成表單控制元件
顧名思義,這個函式就是生成<form>tag的,下面的列表是幾種過載和說明:
BeginForm() |
指向當前的controller和當前view對應的action, http method :Get |
BeginForm(action,controller) |
指定一個controller/action http method : Get |
BeginForm(action,controller,method) |
指定controller/action和 Http method |
BeginForm(action,controller,method,attributes) |
指定controller/action 和 http method ,並設定form的一些屬性(例如 class) |
BeginForm(action,controller,routeValues,method,attributes) |
指定controller/action,設定method,傳route的匿名物件,並可以設定form attribute |
我們可以看到,從第一個到最後一個過載,我們可以自定義的事情越來越多,現在以一個最複雜的最後一個為例,看看生成的form是怎樣的,這樣我們可以考慮不同場景來決定是否使用這個方法:
@using (Html.BeginForm("CreatePerson", "Person",
new { id = "MyIdValue" }, FormMethod.Post,
new { @class = "personClass",data_formType="person"})) {
////html
}
生成的form
<form action="/Person/CreatePerson/MyIdValue" class="personClass" data-formType="person"
method="post">
生成form時,specify一個路由
如果有需要,告訴mvcframework在生成表單時,按照指定的路由配置來生成,也是可以做到的:
對於路由配置:
routes.MapRoute(
name: "FormRoute",
url: "app/forms/{controller}/{action}"
);
編寫View程式碼
@using(Html.BeginRouteForm("FormRoute", new {},FormMethod.Post,
new { @class = "personClass",data_formType="person"})) {
//html
}
生成的表單html(Form部分)
<form action="/app/forms/Person/CreatePerson" class="personClass" data-formType="person"method="post"><div class="dataElem">
這樣就完成了手動根據路由來生成form的實現。
使用html helper
常用的用於html helper生成控制元件
CheckBox |
Html.CheckBox(“name”,false) |
<input id="myCheckbox" name="myCheckbox" type="checkbox" value="true" /> <input name="myCheckbox" type="hidden" value="false" /> |
Hidden Field |
Html.Hidden("myHidden", "val") |
<input id="myHidden" name="myHidden" type="hidden" value="val" /> |
Radio Button |
Html.RadioButton("myRadiobutton", "val", true) |
<input checked="checked" id="myRadiobutton" name="myRadiobutton" type="radio" value="val" /> |
Password |
Html.Password("myPassword", "val") |
<input id="myPassword" name="myPassword" type="password" value="val" /> |
Text Area |
Html.TextArea("myTextarea", "val", 5, 20, null) |
<textarea cols="20" id="myTextarea" name="myTextarea" rows="5"> val</textarea> |
Text box |
Html.TextBox("myTextbox", "val") |
<input id="myTextbox" name="myTextbox" type="text" value="val" /> |
使用html helper重構剛才的View
@using(Html.BeginRouteForm("FormRoute",new {}, FormMethod.Post,
new { @class ="personClass", data_formType="person"})) {
<div class="dataElem">
<label>PersonId</label>
@Html.TextBox("personId",@Model.PersonId)
</div>
<div class="dataElem">
<label>FirstName</label>
@Html.TextBox("firstName",@Model.FirstName)
</div>
<div class="dataElem">
<label>LastName</label>
@Html.TextBox("lastName",@Model.LastName)
</div>
<input type="submit" value="Submit" />
}
執行,檢視瀏覽器生成的html:
<form action="/app/forms/Person/CreatePerson" class="personClass" data-formType="person" method="post"><div class="dataElem">
<label>PersonId</label>
<input 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="text"value="0" />
</div>
<div class="dataElem">
<label>FirstName</label>
<input id="firstName" name="firstName" type="text"value="" />
</div>
<div class="dataElem">
<label>LastName</label>
<input id="lastName" name="lastName" type="text" value="" />
</div>
<input type="submit" value="Submit" />
</form>
結果:
1.form標籤按照我們希望的方式生成了(根據路由配置生成)
2.htmlhelper生成了input,每個都有name,名稱為我們傳入的那個名字。
關於Html.TextBox(“”),mvcframework會分別從ViewData, ViewBag,和@Model.DataValue中查詢指定的property名稱,找到第一個就直接返回,完成匹配。
強型別的form表單
如果使用Html.TextBox(“name”)傳字串不太滿意,希望傳一個Member表示式,可以使用Html.TextBoxFor來達到目的:
支援強型別生成html控制元件的常用方法
CheckBox |
Html.CheckBoxFor(x => x.IsApproved) |
<input id="IsApproved" name="IsApproved" type="checkbox" value="true" /> <input name="IsApproved" type="hidden" value="false" /> |
HiddenField |
Html.HiddenFor(x => x.FirstName) |
<input id="FirstName" name="FirstName" type="hidden" value="" /> |
RadioButton |
Html.RadioButtonFor(x => x.IsApproved, "val") |
<input id="IsApproved" name="IsApproved" type="radio" value="val" /> |
Password |
Html.PasswordFor(x => x.Password) |
<input id="Password" name="Password" type="password" /> |
Text Area |
Html.TextAreaFor(x => x.Bio, 5, 20, new{}) |
<textarea cols="20" id="Bio" name="Bio" rows="5">Bio value</textarea> |
Text Box |
Html.TextBoxFor(x => x.FirstName) |
<input id="FirstName" name="FirstName" type="text" value="" /> |
幾乎和字串版本的類似,區別就是簽名多了一個For,接收的引數為一個MemberExpression。
使用強型別的版本重構我們的View:
@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>
<input type="submit"value="Submit" />
}
驗證一下瀏覽器生成的html是我們希望的:
<form action="/app/forms/Person/CreatePerson" class="personClass" data-formType="person" method="post"><div class="dataElem">
<label>PersonId</label>
<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="text" value="0" />
<div class="dataElem">
<label>First Name</label>
<input id="FirstName" name="FirstName" type="text" value="" />
</div>
<div class="dataElem">
<label>Last Name</label>
<input id="LastName" name="LastName" type="text" value="" />
</div>
<input type="submit" value="Submit" />
</form>
可以看到,mvcframework為我們生成了希望的html。
DropDownList
前面的例子沒有提到Dropdown,是希望以其為特例演示一下強型別的顯示。
1.先看一下html自帶的關於drop-down生成的方法
Html.DropDownList("myList", new SelectList(new [] {"A", "B"}), "Choose") |
<select id="myList" name="myList"> <option value="">Choose</option> <option>A</option> <option>B</option> </select> |
Html.DropDownListFor(x => x.Gender, new SelectList(new [] {"M", "F"})) |
<select id="Gender" name="Gender"> <option>M</option> <option>F</option> </select> |
Html.ListBox("myList", new MultiSelectList(new [] {"A", "B"})) |
<select id="myList" multiple="multiple" name="myList"> <option>A</option> <option>B</option> </select> |
Html.ListBoxFor(x => x.Vals, new MultiSelectList(new [] {"A", "B"})) |
<select id="Vals" multiple="multiple" name="Vals"> <option>A</option> <option>B</option> </select> |
可以看到,mvc framework提供了不同的過載,我們可以生成multiselect和select。
生成帶有role的表單,修改View程式碼:
@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" />
}
這次我們新增了Role,我們使用了Enum的靜態方法獲得指定Enum型別中的所有值。傳遞給mvcframework的是Member Expression和string array。
測試:
瀏覽器生成的select標籤
<div class="dataElem">
<label>Role</label>
<select data-val="true" data-val-required="The Role field isrequired." id="Role" name="Role">
<option selected="selected">Admin</option>
<option>User</option>
<option>Guest</option>
</select>
</div>
相關文章
- 【webpack 系列】進階篇Web
- React進階篇1React
- asp.net core 系列之Response caching(1)ASP.NET
- 測開之函式進階· 第1篇《遞迴函式》函式遞迴
- Asp.NetCore之AutoMapper進階篇ASP.NETNetCoreAPP
- 正規表示式系列之中級進階篇
- Dagger 2 系列(五) -- 進階篇:@Scope 和 @Singleton
- 帶你深度解鎖Webpack系列(進階篇)Web
- 你所不知道的ASP.NET Core進階系列(三)ASP.NET
- Linux ACL 許可權之進階篇Linux
- Java多執行緒之進階篇Java執行緒
- Java進階篇 設計模式之十四 ----- 總結篇Java設計模式
- 高階前端進階系列 - webview前端WebView
- Three.js進階篇之6 - 碰撞檢測JS
- Three.js進階篇之5 - 粒子系統JS
- [一天一個進階系列] - MyBatis基礎篇MyBatis
- asp.net core 系列之ConfigurationASP.NET
- asp.net core 系列之StartupASP.NET
- redis進階之快取管理(1課時)Redis快取
- React進階篇2React
- Android 進階 ———— Handler系列之建立子執行緒HandlerAndroid執行緒
- python入門與進階篇(七)之原生爬蟲Python爬蟲
- vue2進階篇:vue-router之命名路由Vue路由
- 【進階篇】Redis實戰之Jedis使用技巧詳解Redis
- Java進階篇之十五 ----- JDK1.8的Lambda、StreJavaJDK
- 測開之函式進階· 第6篇《閉包》函式
- Java進階學習之集合與泛型(1)Java泛型
- JS進階系列 --- 繼承JS繼承
- python網路進階篇Python
- 介面測試進階篇
- 樹莓派-進階篇樹莓派
- 《MySQL 進階篇》二十:鎖MySql
- Vue 進階系列(一)之響應式原理及實現Vue
- Vue 進階系列(三)之Render函式原理及實現Vue函式
- mysql 開發進階篇系列 42 邏輯備份與恢復MySql
- Three.js進階篇之7 - 3D宇宙特效JS3D特效
- JUnit5學習之八:綜合進階(終篇)
- 測開之函式進階· 第2篇《純函式》函式
- 測開之函式進階· 第4篇《匿名函式》函式