Nguon: Microsoft Learn · .NET 8.0

Input Tag Helper trong ASP.NET Core

Nguồn: Tag Helpers in forms in ASP.NET Core - The Input Tag Helper

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:

cshtml
<input asp-for="<Expression Name>">

Input Tag Helper thực hiện những việc sau:

``` 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 .NETKiểu Input
Booltype="checkbox"
Stringtype="text"
DateTimetype="datetime-local"
Bytetype="number"
Inttype="number"
Single, Doubletype="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ínhKiể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ụ:

csharp
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; }
    }
}
cshtml
@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:

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 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 EmailPassword 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:

cshtml
<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:

html
<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:

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:

csharp
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.TextBoxHtml.TextBoxFor thì không. Html.EditorHtml.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.TextBoxHtml.Editor thì không (sử dụng tên biểu thức).

HtmlAttributes

@Html.Editor()@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.

cshtml
@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):

cshtml
@{
  var joe = "Joe";
}

<input asp-for="@joe">

Tạo ra:

html
<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ừ:

Đ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:

csharp
public class AddressViewModel
{
    public string AddressLine1 { get; set; }
}
csharp
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:

cshtml
@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:

html
<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:

csharp
public class Person
{
    public List<string> Colors { get; set; }

    public int Age { get; set; }
}

Action method (phương thức action):

csharp
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ể:

cshtml
@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:

cshtml
@model string

<label asp-for="@Model"></label>
<input asp-for="@Model" /> <br />

Ví dụ sử dụng List<T>:

csharp
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:

cshtml
@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:

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.