在 ASP.Net Core 微軟提供了 TagHelper 來更精簡我們在寫 View 的語法,整個使用上會比較清爽,而我們也可以很方便的自訂和擴充,但是在實做擴充上想可以支援 asp-for 卻卡住了,後來找了討論和文章,要支援的化需要使用 ModelExpression,因此針對 ModelExpression 的實做上寫了這篇實做的記錄。
前言
在 ASP.Net Core 微軟提供了 TagHelper 來更精簡我們在寫 View 的語法,整個使用上會比較清爽,而我們也可以很方便的自訂和擴充,但是在實做擴充上想可以支援 asp-for 卻卡住了,後來找了討論和文章,要支援的化需要使用 ModelExpression,因此針對 ModelExpression 的實做上寫了這篇實做的記錄。
實做的情境為表單的欄位名稱,預設只有單純顯示 label,而我想要可以在 Model 設定為必填欄位時候可以自動加上 * 來顯示,因此就可以用上 ModelExpression 來實做這樣的需求了。
實做
首先定義一個 LoginViewModel
並且設定好相關的 Attribute。
public class LoginViewModel
{
/// <summary>
/// 帳號
/// </summary>
[Display(Name = "帳號", Description = "請不要輸入 Admin 作為帳號!")]
[Required(ErrorMessage = "請輸入 {0}")]
public string Account { get; set; }
/// <summary>
/// 密碼
/// </summary>
[Display(Name = "密碼")]
[Required(ErrorMessage = "請輸入 {0}")]
[DataType(DataType.Password)]
public string Password { get; set; }
}
再來實做 DisplayTitleTaghelper
,裡面定義了 ModelExpression
的 aspFor
屬性,另外也加入了 ViewContext ,到時候可以直接呼叫內建的 TagHelper 方法來建立內容。程式重點在透過 aspFor.Metadata 來取得我們在 Model 設定的 Attribute,拿到 Metadata 之後就可以取得設定的 IsRequired
、DisplayName
、Description
,有了這些之後剩下的就單純用 TagBuilder 來組出我們要的 HTML了。
public class DisplayTitleTagHelper : TagHelper
{
public ModelExpression aspFor { get; set; }
[ViewContext]
[HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }
protected IHtmlGenerator _generator { get; set; }
public DisplayTitleTagHelper(IHtmlGenerator generator)
{
_generator = generator;
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "";
var propMetadata = aspFor.Metadata;
var @class = context.AllAttributes["class"].Value;
var label = _generator.GenerateLabel(ViewContext, aspFor.ModelExplorer,
propMetadata.Name, propMetadata.Name, new { @class });
var strong = new TagBuilder("strong");
strong.InnerHtml.Append(propMetadata.DisplayName);
label.InnerHtml.Clear();
label.InnerHtml.AppendHtml(strong);
if (propMetadata.IsRequired)
{
var span = new TagBuilder("span");
span.AddCssClass("text-danger");
span.InnerHtml.Append("*");
label.InnerHtml.AppendHtml(span);
}
output.Content.AppendHtml(label);
if (string.IsNullOrEmpty(propMetadata.Description) == false)
{
var span = new TagBuilder("span");
span.AddCssClass("text-success");
span.InnerHtml.Append(propMetadata.Description);
output.Content.AppendHtml(span);
}
var validation = _generator.GenerateValidationMessage(ViewContext, aspFor.ModelExplorer,
propMetadata.Name, string.Empty, string.Empty, new { @class = "text-danger" });
output.Content.AppendHtml(validation);
base.Process(context, output);
}
}
寫好之後就是使用了,首先就是要記得在 View 上加上 @addTagHelper "*, TagHelperForModel"
,這樣程式才會認得我們自定義的標籤,而在 View 上使用我們設定的標籤的語法如下,注意的點是標籤名稱是 display-title
。
<div class="row">
<div class="col-md-4">
<form asp-action="Login">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<display-title asp-for="Account" class="control-label"></display-title>
<input asp-for="Account" class="form-control" />
</div>
<div class="form-group">
<display-title asp-for="Password" class="control-label"></display-title>
<input asp-for="Password" class="form-control" />
</div>
<div class="form-group">
<input type="submit" value="Login" class="btn btn-primary" />
</div>
</form>
</div>
</div>
再來就是執行結果,如預期的呈獻我們要的 HTML ,顯示上也如預期,必填欄位自動加入 *,有設定 Description 也會顯示出來,並且也加上的驗證的區塊。
完整程式碼請參考 GitHub。
結論
微軟在 ASP.Net Core 提供的 TagHelper,讓我們在編輯 View 的時候會更接近 HTML,因此就可以更方便讓前端人員配合來使用,透過自訂也可以有更多的可能性,也可以封裝成 DLL 之後更方便重複使用。