TagHelper
TagHelper是ASP.NET 5的一個新特性。也許在你還沒有聽說過它的時候, 它已經在技術人員之間引起了大量討論,甚至有一部分稱它為服務器控件的回歸。實際上它只不過是一個簡化版本,把HTML和服務器內容混合在一起,沒有控件生命周期,狀態保持和事件。它不像服務器控件那樣,對頁面所有內容都具有訪問權限。它只能訪問到自己所生成的內容。
在 Razor 文件中,Tag Helpers 能夠讓服務端代碼參與創建和渲染 HTML 元素。
Tag Helpers通過豐富的智能感知環境來創建 HTML 和 Razor 標記,為我們提供了友好的開發體驗,同時讓生成的代碼更加高效、可靠,可維護。
Form TagHelper
直接舉例:
<form asp-controller="Demo" asp-action="Register" method="post"> </form>
生成的HTML:
<form method="post" action="/Demo/Register"> <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>" /> </form>
MVC 運行時會根據 Form Tag Helper 的屬性 asp-controller 和 asp-action 生成 action的屬性值。
命名路由
通過asp-route可以讓下面的表單使用路由規則中name為register的路由。
<form asp-route="register" method="post"> <!-- Input and Submit elements --> </form>
生成的HTML:
<form asp-controller="Account" asp-action="Login" method="post" class="form-horizontal" role="form"> <!-- Input and Submit elements --> </form>
Input TagHelper
作用:
-
為 asp-for 屬性中指定的表達式名稱生成 id 和 name 屬性。
-
基於模型類型和應用在模型屬性上的 Tpye 特性來設置 HTML type 的屬性值。
-
如果 HTML type 屬性已被指定,則不會覆蓋它。
-
根據應用在模型屬性上的 驗證 特性生成 HTML5 驗證屬性。
上面第2條說到TagHelper基於 .NET 類型和特性來設置 HTML type 屬性。以下是常見的 .NET 類型和特性生成出的 HTML 類型:
.Net類型生成的HTML類型:
.Net類型 | Input類型 |
---|---|
Bool | type=”checkbox” |
String | type=”text” |
DateTime | type=”datetime” |
Byte | type=”number” |
Int | type=”number” |
Single, Double | type=”number” |
.Net特性生成的HTML類型:
Attribute | Input Type |
---|---|
[EmailAddress] | type=”email” |
[Url] | type=”url” |
[HiddenInput] | type=”hidden” |
[Phone] | type=”tel” |
[DataType(DataType.Password)] | type=”password” |
[DataType(DataType.Date)] | type=”date” |
[DataType(DataType.Time)] | type=”time” |
例子:
public class RegisterViewModel { [Required] [EmailAddress] [Display(Name = "Email Address")] public string Email { get; set; } [Required] [DataType(DataType.Password)] public string Password { get; set; } }
@model RegisterViewModel
<form asp-controller="Demo" asp-action="RegisterInput" method="post"> Email: <input asp-for="Email" /> <br /> Password: <input asp-for="Password" /><br /> <button type="submit">Register</button> </form>
上述代碼生成如下的 HTML :
<form method="post" action="/Demo/RegisterInput"> Email: <input type="email" data-val="true" data-val-email="The Email Address field is not a valid e-mail address." data-val-required="The Email Address field is required." id="Email" name="Email" value="" /> <br> Password: <input type="password" data-val="true" data-val-required="The Password field is required." id="Password" name="Password" /><br> <button type="submit">Register</button> <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>" /> </form>
asp-for 屬性值是一個 ModelExpression 同時也是 lambda 表達式右邊的部分。因此,不需要使用 Model 前綴。
定位子屬性
asp-for可以通過視圖模型的屬性路徑定位到子屬性。
類代碼:
//類A public class AddressViewModel { public string AddressLine { get; set; } } //類B中嵌套了類A public class RegisterAddressViewModel { public string Email { get; set; } [DataType(DataType.Password)] public string Password { get; set; } public AddressViewModel Address { get; set; } }
視圖代碼:
@model RegisterAddressViewModel
<form asp-controller="Demo" asp-action="RegisterAddress" method="post"> Email: <input asp-for="Email" /> <br /> Password: <input asp-for="Password" /><br /> Address: <input asp-for="Address.AddressLine" /><br /> <button type="submit">Register</button> </form>
asp-for是不需要model前綴的,所以直接可以與model中的email等屬性綁定。
對於子屬性AddressLine,我們可以通過Address.AddressLine來綁定。
TagHelper驗證
<span asp-validation-for="Email"></span> <span class="field-validation-valid" data-valmsg-for="Email" data-valmsg-replace="true"> </span>
通常在模型屬性相同的 Input Tag Helper后面使用 Validation Message Tag Helper 。這樣可以在發生驗證錯誤的 input 旁邊顯示錯誤信息。
HTML Helper 替代選項: Html.ValidationMessageFor()
Select TagHelper
Select TagHelper 的 asp-for 為 select 元素指定模型的屬性名稱,而 asp-items 則指定 option 元素。
類代碼:
public class CountryViewModel { public string Country { get; set; } public List<SelectListItem> Countries { get; } = new List<SelectListItem> { new SelectListItem { Value = "MX", Text = "Mexico" }, new SelectListItem { Value = "CA", Text = "Canada" }, new SelectListItem { Value = "US", Text = "USA" }, }; } public IActionResult Index() { var model = new CountryViewModel(); model.Country = "CA"; return View(model); }
index視圖:
@model CountryViewModel
<form asp-controller="Home" asp-action="Index" method="post"> <select asp-for="Country" asp-items="Model.Countries"></select> <br /><button type="submit">Register</button> </form>
生成Html:
<form method="post" action="/"> <select id="Country" name="Country"> <option value="MX">Mexico</option> <option selected="selected" value="CA">Canada</option> <option value="US">USA</option> </select> <br /><button type="submit">Register</button> <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>" /> </form>
上面asp-for指定的模型類型是單值類型,但如果指定的模型換成一個 IEnumerable 類型, Select Tag Helper 將會在HTML中自動生成 multiple = “multiple”。
注意:只要asp-for 是一個特例,不需要 Model 前綴。
選項分組
當選項分組時,會生成 HTML < optgroup > 元素:
public class CountryViewModelGroup { public CountryViewModelGroup() { var AmericaGroup = new SelectListGroup { Name = "America" }; var EuropeGroup = new SelectListGroup { Name = "Europe" }; public string Country { get; set; } public List<SelectListItem> Countries { get; } = new List<SelectListItem> { new SelectListItem { Value = "CAN", Text = "Canada", Group = AmericaGroup }, new SelectListItem { Value = "US", Text = "USA", Group = AmericaGroup }, new SelectListItem { Value = "FR", Text = "France", Group = EuropeGroup }, new SelectListItem { Value = "ES", Text = "Spain", Group = EuropeGroup }, }; } }
其余操作與上面Select Tag Helper類似。。。
Select TagHelper的枚舉綁定
public enum CountryEnum { Mexico, [Display(Name = "United States of America")] USA, Canada, France, Germany, Spain } public class CountryEnumViewModel { public CountryEnum EnumCountry { get; set; } }
視圖:
@model CountryEnumViewModel
<form asp-controller="Home" asp-action="IndexEnum" method="post"> <select asp-for="EnumCountry" asp-items="Html.GetEnumSelectList<CountryEnum>()"> > </select> <br /><button type="submit">Register</button> </form>
生成HTML:
<form method="post" action="/Home/IndexEnum"> <select data-val="true" data-val-required="The EnumCountry field is required." id="EnumCountry" name="EnumCountry"> <option value="0">Mexico</option> <option value="1">United States of America</option> <option value="2">Canada</option> <option value="3">France</option> <option value="4">Germany</option> <option selected="selected" value="5">Spain</option> </select> <br /><button type="submit">Register</button> <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>" /> </form>
Html Helper與 Input Tag Helper 功能的異同
-
Html.TextBox、Html.TextBoxFor、Html.Editor 和 Html.EditorFor 有着與 Input Tag Helper 重復的功能。
-
Input Tag Helper 會自動設置 type 屬性,而Html.TextBox 和 Html.TextBoxFor 則不會。
-
Html.Editor 和 Html.EditorFor 會處理集合、復雜對象以及模版,而Input Tag Helper 則不會。
-
Input Tag Helper 、Html.EditorFor 和 Html.TextBoxFor 是強類型的,但是Html.TextBox 和 Html.Editor 則不是。
-
HTML Helper 的Html.TextAreaFor、Html.LabelFor 與Textarea Tag Helper、 Label Tag Helper 類似。
-
Tag Helper和 HTML Helpers 相比,生成的標記干凈得多而且更容易閱讀,編輯和維護。
自定義Tag
創建類繼承自TagHelper可以讓我們擴展tagHelper的功能:
<email>Support</email>
- 1
//[HtmlTargetElement(Attributes = "email")] public class EmailTagHelper : TagHelper { private const string EmailDomain = "contoso.com"; public override void Process(TagHelperContext context, TagHelperOutput output) { output.TagName = "a"; // Replaces <email> with <a> tag var address = MailTo + "@" + EmailDomain; output.Attributes.SetAttribute("href", "mailto:" + address); output.Content.SetContent(address); } }
Description:
-
Tag helper 使用以目標元素名作為根類名,在這個例子中, EmailTagHelper 的根名稱是 email ,因此 TagName的默認值就是email。
-
重寫 Process 方法可以控制 Tag Helper 在執行過程中的行為。 TagHelper 類同樣提供了相同參數的異步版本(ProcessAsync)。
-
類名后綴TagHelper是非必需的,但它被認為是最佳慣例約定。
-
為使自定義的TagHelper在Razor中可用,需要在Views/_ViewImports.cshtml 文件中通過addTagHelper指令添加包含自定義TagHelper的命名空間。
-
[HtmlTargetElement] 屬性傳遞一個屬性參數,指定為任何 HTML 元素包含名為 “bold” 的 HTML 屬性。例如:同時使用[HtmlTargetElement(“email”)]和[HtmlTargetElement(Attributes = “email”)],bold 標簽和bold 屬性都會被匹配。
//異步Process public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { output.TagName = "a"; // Replaces <email> with <a> tag var content = await output.GetChildContentAsync(); var target = content.GetContent() + "@" + EmailDomain; output.Attributes.SetAttribute("href", "mailto:" + target); output.Content.SetContent(target); }