Input Tag Helper trong ASP.NET Core
Input Tag Helper (trình trợ giúp thẻ đầu vào) liên kết một phần tử HTML <input> với một biểu thức model (mô hình) trong Razor view (khung nhìn).
Cú pháp:
<input asp-for="<Expression Name>">
Input Tag Helper thực hiện những việc sau:
- Tạo ra các thuộc tính HTML
idvànamecho tên biểu thức được chỉ định trong thuộc tínhasp-for.asp-for="Property1.Property2"tương đương vớim => m.Property1.Property2. Tên biểu thức là giá trị được sử dụng cho thuộc tínhasp-for. - Thiết lập giá trị thuộc tính HTML
typedựa trên kiểu dữ liệu model và các thuộc tính data annotation được áp dụng cho thuộc tính model. - Sẽ không ghi đè giá trị thuộc tính HTML
typekhi đã được chỉ định. - Tạo ra các thuộc tính validation (xác thực) HTML5 từ các thuộc tính data annotation được áp dụng cho thuộc tính model.
- Có tính năng trùng lặp với HTML Helper
Html.TextBoxForvàHtml.EditorFor. - Cung cấp strong typing (định kiểu mạnh). Nếu tên thuộc tính thay đổi và bạn không cập nhật Tag Helper, bạn sẽ nhận được lỗi tương tự:
``` An error occurred during the compilation of a resource required to process this request. Please review the following specific error details and modify your source code appropriately.
Type expected 'RegisterViewModel' does not contain a definition for 'Email' and no extension method 'Email' accepting a first argument of type 'RegisterViewModel' could be found (are you missing a using directive or an assembly reference?) ```
Input Tag Helper thiết lập thuộc tính HTML type dựa trên kiểu .NET. Bảng sau liệt kê một số kiểu .NET phổ biến và kiểu HTML được tạo ra (không phải tất cả kiểu .NET đều được liệt kê):
| Kiểu .NET | Kiểu Input |
|---|---|
| Bool | type="checkbox" |
| String | type="text" |
| DateTime | type="datetime-local" |
| Byte | type="number" |
| Int | type="number" |
| Single, Double | type="number" |
Bảng sau liệt kê một số thuộc tính data annotation phổ biến mà Input Tag Helper sẽ ánh xạ đến các kiểu input cụ thể (không phải tất cả thuộc tính validation đều được liệt kê):
| Thuộc tính | Kiểu Input |
|---|---|
| [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" |
Ví dụ:
using System.ComponentModel.DataAnnotations;
namespace FormsTagHelper.ViewModels
{
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">
<label>Email: <input asp-for="Email" /></label> <br />
<label>Password: <input asp-for="Password" /></label><br />
<button type="submit">Register</button>
</form>Đoạn code trên tạo ra HTML sau:
<form method="post" action="/Demo/RegisterInput">
Email:
<input type="email" data-val="true"
data-val-email="The Email Address field is not a valid email 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>Các data annotation được áp dụng cho thuộc tính Email và Password tạo ra metadata (siêu dữ liệu) trên model. Input Tag Helper sử dụng metadata đó và tạo ra các thuộc tính data-val-* của HTML5 (xem Model Validation). Các thuộc tính này mô tả các validator (trình xác thực) gắn vào các trường input. Điều này cung cấp xác thực HTML5 và jQuery không xâm phạm. Các thuộc tính không xâm phạm có dạng data-val-rule="Error Message", trong đó rule là tên quy tắc validation (ví dụ: data-val-required, data-val-email, data-val-maxlength, v.v.).
Khi liên kết nhiều control input đến cùng một thuộc tính, các control được tạo ra sẽ dùng chung id, điều này làm markup (mã đánh dấu) không hợp lệ. Để tránh trùng lặp, hãy chỉ định thuộc tính id cho mỗi control một cách tường minh.
Rendering input ẩn cho Checkbox
Checkbox trong HTML5 không gửi giá trị khi chúng không được chọn. Để kích hoạt việc gửi giá trị mặc định cho checkbox không được chọn, Input Tag Helper tạo ra một input ẩn bổ sung cho checkbox.
Ví dụ, xét Razor markup sau sử dụng Input Tag Helper cho thuộc tính model boolean IsChecked:
<form method="post">
<input asp-for="@Model.IsChecked" />
<button type="submit">Submit</button>
</form>Razor markup trên tạo ra HTML markup tương tự như sau:
<form method="post">
<input name="IsChecked" type="checkbox" value="true" />
<button type="submit">Submit</button>
<input name="IsChecked" type="hidden" value="false" />
</form>HTML trên cho thấy một input ẩn bổ sung có tên IsChecked và giá trị false. Khi form được gửi:
- Nếu checkbox
IsCheckedđược chọn, cảtruevàfalseđều được gửi làm giá trị. - Nếu checkbox
IsCheckedkhông được chọn, chỉ có giá trị input ẩnfalseđược gửi.
Quá trình model-binding (liên kết model) của ASP.NET Core chỉ đọc giá trị đầu tiên khi liên kết đến giá trị bool, dẫn đến true cho checkbox được chọn và false cho checkbox không được chọn.
Để cấu hình hành vi rendering (kết xuất) của input ẩn, đặt thuộc tính CheckBoxHiddenInputRenderMode trên MvcViewOptions.HtmlHelperOptions:
services.Configure<MvcViewOptions>(options =>
options.HtmlHelperOptions.CheckBoxHiddenInputRenderMode =
CheckBoxHiddenInputRenderMode.None);Các HTML Helper thay thế cho Input Tag Helper
Html.TextBox, Html.TextBoxFor, Html.Editor, và Html.EditorFor có các tính năng trùng lặp với Input Tag Helper. Input Tag Helper sẽ tự động thiết lập thuộc tính type; Html.TextBox và Html.TextBoxFor thì không. Html.Editor và Html.EditorFor xử lý các collection (tập hợp), complex object (đối tượng phức tạp), và template (mẫu); Input Tag Helper thì không. Input Tag Helper, Html.EditorFor, và Html.TextBoxFor được định kiểu mạnh (sử dụng lambda expression); Html.TextBox và Html.Editor thì không (sử dụng tên biểu thức).
HtmlAttributes
@Html.Editor() và @Html.EditorFor() sử dụng một mục ViewDataDictionary đặc biệt có tên htmlAttributes khi thực thi các template mặc định của chúng. Hành vi này có thể được bổ sung tùy chọn bằng cách sử dụng tham số additionalViewData.
@Html.EditorFor(model => model.YourProperty,
new { htmlAttributes = new { @class="myCssClass", style="Width:100px" } })Tên biểu thức
Giá trị thuộc tính asp-for là một ModelExpression và là vế phải của một lambda expression (biểu thức lambda). Do đó, asp-for="Property1" trở thành m => m.Property1 trong code được tạo ra. Bạn có thể sử dụng ký tự @ để bắt đầu một inline expression (biểu thức nội tuyến):
@{
var joe = "Joe";
}
<input asp-for="@joe">Tạo ra:
<input type="text" id="joe" name="joe" value="Joe">
Với collection property (thuộc tính tập hợp), asp-for="CollectionProperty[23].Member" tạo ra cùng tên với asp-for="CollectionProperty[i].Member" khi i có giá trị 23.
Khi ASP.NET Core MVC tính toán giá trị của ModelExpression, nó kiểm tra nhiều nguồn, bao gồm ModelState. Xét <input type="text" asp-for="Name">. Giá trị thuộc tính value được tính là giá trị không null đầu tiên từ:
- Mục
ModelStatevới key "Name". - Kết quả của biểu thức
Model.Name.
Điều hướng đến thuộc tính con
Bạn cũng có thể điều hướng đến thuộc tính con bằng cách sử dụng property path (đường dẫn thuộc tính) của view model. Xét model class phức tạp hơn chứa thuộc tính Address con:
public class AddressViewModel
{
public string AddressLine1 { get; set; }
}public class RegisterAddressViewModel
{
public string Email { get; set; }
[DataType(DataType.Password)]
public string Password { get; set; }
public AddressViewModel Address { get; set; }
}Trong view, chúng ta liên kết với Address.AddressLine1:
@model RegisterAddressViewModel
<form asp-controller="Demo" asp-action="RegisterAddress" method="post">
<label>Email: <input asp-for="Email" /></label> <br />
<label>Password: <input asp-for="Password" /></label><br />
<label>Address: <input asp-for="Address.AddressLine1" /></label><br />
<button type="submit">Register</button>
</form>HTML sau được tạo ra cho Address.AddressLine1:
<input type="text" id="Address_AddressLine1" name="Address.AddressLine1" value="">
Tên biểu thức và Collection
Ví dụ, một model chứa mảng Colors:
public class Person
{
public List<string> Colors { get; set; }
public int Age { get; set; }
}Action method (phương thức action):
public IActionResult Edit(int id, int colorIndex)
{
ViewData["Index"] = colorIndex;
return View(GetPerson(id));
}Razor sau đây cho thấy cách truy cập một phần tử Color cụ thể:
@model Person
@{
var index = (int)ViewData["index"];
}
<form asp-controller="ToDo" asp-action="Edit" method="post">
@Html.EditorFor(m => m.Colors[index])
<label asp-for="Age"></label>
<input asp-for="Age" /><br />
<button type="submit">Post</button>
</form>Template Views/Shared/EditorTemplates/String.cshtml:
@model string <label asp-for="@Model"></label> <input asp-for="@Model" /> <br />
Ví dụ sử dụng List<T>:
public class ToDoItem
{
public string Name { get; set; }
public bool IsDone { get; set; }
}Razor sau đây cho thấy cách lặp qua một collection:
@model List<ToDoItem>
<form asp-controller="ToDo" asp-action="Edit" method="post">
<table>
<tr> <th>Name</th> <th>Is Done</th> </tr>
@for (int i = 0; i < Model.Count; i++)
{
<tr>
@Html.EditorFor(model => model[i])
</tr>
}
</table>
<button type="submit">Save</button>
</form>Template Views/Shared/EditorTemplates/ToDoItem.cshtml:
@model ToDoItem
<td>
<label asp-for="@Model.Name"></label>
@Html.DisplayFor(model => model.Name)
</td>
<td>
<input asp-for="@Model.IsDone" />
</td>Nên sử dụng foreach nếu có thể khi giá trị sẽ được dùng trong context asp-for hoặc tương đương Html.DisplayFor. Nói chung, for tốt hơn foreach (nếu kịch bản cho phép) vì nó không cần cấp phát enumerator (bộ liệt kê); tuy nhiên, việc đánh giá indexer trong biểu thức LINQ có thể tốn kém và nên được giảm thiểu.