Nguon: Microsoft Learn · .NET 8.0

Tag Helpers trong Forms trong ASP.NET Core

Nguồn: Tag Helpers in forms in ASP.NET Core

Bởi Rick Anderson, N. Taylor Mullen, Dave Paquette, và Jerrie Pelser

Tài liệu này hướng dẫn cách làm việc với Forms (biểu mẫu) và các phần tử HTML thường dùng trong Form. Phần tử HTML Form cung cấp cơ chế chính mà các ứng dụng web dùng để gửi dữ liệu ngược về server. Phần lớn tài liệu này mô tả Tag Helpers và cách chúng giúp bạn tạo các HTML form mạnh mẽ một cách hiệu quả. Bạn nên đọc Giới thiệu về Tag Helpers trước khi đọc tài liệu này.

Trong nhiều trường hợp, HTML Helpers cung cấp một cách tiếp cận thay thế cho một Tag Helper cụ thể. Tuy nhiên, điều quan trọng cần nhận ra là Tag Helpers không thay thế HTML Helpers, và không có Tag Helper cho mỗi HTML Helper. Khi có HTML Helper thay thế, nó sẽ được đề cập.

Form Tag Helper

Form Tag Helper:

Ví dụ:

cshtml
<form asp-controller="Demo" asp-action="Register" method="post">
    <!-- Input and Submit elements -->
</form>

Form Tag Helper ở trên tạo ra HTML sau:

html
<form method="post" action="/Demo/Register">
    <!-- Input and Submit elements -->
    <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
</form>

MVC runtime tạo giá trị thuộc tính action từ các thuộc tính asp-controllerasp-action của Form Tag Helper. Form Tag Helper cũng tạo hidden Request Verification Token để ngăn chặn cross-site request forgery (khi dùng với thuộc tính [ValidateAntiForgeryToken] trong HTTP Post action method). Bảo vệ một HTML Form thuần khỏi cross-site request forgery rất khó, nhưng Form Tag Helper cung cấp dịch vụ này cho bạn.

Sử dụng named route

Thuộc tính Tag Helper asp-route cũng có thể tạo markup cho thuộc tính action của HTML. Một ứng dụng có route tên là register có thể dùng markup sau cho trang đăng ký:

cshtml
<form asp-route="register" method="post">
    <!-- Input and Submit elements -->
</form>

Nhiều view trong thư mục Views/Account (được tạo khi bạn tạo ứng dụng web mới với Individual Accounts) chứa thuộc tính asp-route-returnurl:

cshtml
<form asp-controller="Account" asp-action="Login"
     asp-route-returnurl="@ViewData["ReturnUrl"]"
     method="post" class="form-horizontal" role="form">

Với các template tích hợp sẵn, returnUrl chỉ được điền tự động khi bạn cố gắng truy cập tài nguyên được ủy quyền nhưng chưa được xác thực hoặc ủy quyền. Khi bạn cố truy cập trái phép, middleware bảo mật chuyển hướng bạn đến trang đăng nhập với returnUrl được đặt.

Form Action Tag Helper

Form Action Tag Helper tạo thuộc tính formaction trên thẻ <button ...> hoặc <input type="image" ...> được tạo ra. Thuộc tính formaction điều khiển nơi form gửi dữ liệu. Nó liên kết với các phần tử <input> kiểu image và các phần tử <button>. Form Action Tag Helper cho phép sử dụng nhiều thuộc tính asp- của AnchorTagHelper để kiểm soát liên kết formaction nào được tạo cho phần tử tương ứng.

Các thuộc tính AnchorTagHelper được hỗ trợ để kiểm soát giá trị formaction:

Thuộc tínhMô tả
asp-controllerTên của controller.
asp-actionTên của action method.
asp-areaTên của area.
asp-pageTên của Razor page.
asp-page-handlerTên của Razor page handler.
asp-routeTên của route.
asp-route-{value}Một giá trị URL route đơn. Ví dụ: asp-route-id="1234".
asp-all-route-dataTất cả route values.
asp-fragmentFragment của URL.

Ví dụ gửi đến controller

Markup sau gửi form đến action Index của HomeController khi input hoặc button được chọn:

cshtml
<form method="post">
    <button asp-controller="Home" asp-action="Index">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" asp-controller="Home" 
                                asp-action="Index">
</form>

Markup trên tạo ra HTML sau:

html
<form method="post">
    <button formaction="/Home">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" formaction="/Home">
</form>

Ví dụ gửi đến page

Markup sau gửi form đến Razor Page About:

cshtml
<form method="post">
    <button asp-page="About">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" asp-page="About">
</form>

Markup trên tạo ra HTML sau:

html
<form method="post">
    <button formaction="/About">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" formaction="/About">
</form>

Ví dụ gửi đến route

Xét endpoint /Home/Test:

csharp
public class HomeController : Controller
{
    [Route("/Home/Test", Name = "Custom")]
    public string Test()
    {
        return "This is the test page";
    }
}

Markup sau gửi form đến endpoint /Home/Test.

cshtml
<form method="post">
    <button asp-route="Custom">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" asp-route="Custom">
</form>

Markup trên tạo ra HTML sau:

html
<form method="post">
    <button formaction="/Home/Test">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" formaction="/Home/Test">
</form>

Input Tag Helper

Input Tag Helper liên kết một phần tử HTML <input> với một model expression trong Razor view.

Cú pháp:

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

Input Tag Helper:

``` 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 đặt 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 input HTML được tạo (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 hiển thị một số thuộc tính data annotations phổ biến mà input tag helper sẽ ánh xạ sang các kiểu input cụ thể:

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>

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 annotations được áp dụng cho các thuộc tính EmailPassword tạo metadata trên model. Input Tag Helper sử dụng metadata model và tạo 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 được đính kèm vào input fields. Điều này cung cấp xác thực HTML5 không xâm phạm và jQuery. Các thuộc tính không xâm phạm có định dạng data-val-rule="Error Message", trong đó rule là tên quy tắc xác thực (ví dụ: data-val-required, data-val-email, data-val-maxlength, v.v.).

Khi liên kết nhiều điều khiển input với cùng một thuộc tính, các điều khiển được tạo ra sẽ dùng chung id, làm cho markup được tạo không hợp lệ. Để ngăn chặn trùng lặp, hãy chỉ định thuộc tính id cho mỗi điều khiển một cách rõ ràng.

Hiển thị hidden input cho Checkbox

Checkbox trong HTML5 không gửi giá trị khi chưa được chọn. Để cho phép gửi giá trị mặc định cho checkbox chưa chọn, Input Tag Helper tạo thêm một hidden input cho checkbox.

Ví dụ, xét Razor markup sau 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 markup trên hiển thị thêm một hidden input với tên IsChecked và giá trị false. Mặc định, hidden input này được hiển thị ở cuối form. Khi form được submit:

Quá trình model-binding của ASP.NET Core chỉ đọc giá trị đầu tiên khi liên kết với giá trị bool, kết quả là true cho checkbox được chọn và false cho checkbox không được chọn.

Để cấu hình hành vi hiển thị hidden input, đặt thuộc tính CheckBoxHiddenInputRenderMode trên MvcViewOptions.HtmlHelperOptions. Ví dụ:

csharp
services.Configure<MvcViewOptions>(options =>
    options.HtmlHelperOptions.CheckBoxHiddenInputRenderMode =
        CheckBoxHiddenInputRenderMode.None);

Code trên vô hiệu hóa hiển thị hidden input cho checkbox bằng cách đặt CheckBoxHiddenInputRenderMode thành CheckBoxHiddenInputRenderMode.None.

HTML Helper alternatives 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 đặt thuộc tính type; Html.TextBoxHtml.TextBoxFor thì không. Html.EditorHtml.EditorFor xử lý collections, complex objects, và templates; Input Tag Helper không làm vậy. Input Tag Helper, Html.EditorFor, và Html.TextBoxFor là strongly typed (chúng dùng lambda expressions); Html.TextBoxHtml.Editor thì không (chúng dùng expression names).

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 dùng tham số additionalViewData. Khóa "htmlAttributes" không phân biệt hoa thường.

cshtml
@Html.EditorFor(model => model.YourProperty, 
  new { htmlAttributes = new { @class="myCssClass", style="Width:100px" } })

Tên Expression

Giá trị thuộc tính asp-for là một ModelExpression và phần bên phải của lambda expression. Do đó, asp-for="Property1" trở thành m => m.Property1 trong code được tạo, đó là lý do tại sao bạn không cần tiền tố Model. Bạn có thể dùng ký tự "@" để bắt đầu inline expression và di chuyển trước m.:

cshtml
@{
  var joe = "Joe";
}

<input asp-for="@joe">

Tạo ra:

html
<input type="text" id="joe" name="joe" value="Joe">

Với collection properties, asp-for="CollectionProperty[23].Member" tạo ra tên giống như asp-for="CollectionProperty[i].Member" khi i có giá trị 23.

Khi ASP.NET Core MVC tính 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 toán 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 các thuộc tính con bằng cách dùng property path của view model. Xét một model class phức tạp hơn có 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 cho Address.AddressLine1:

html
<input type="text" id="Address_AddressLine1" name="Address.AddressLine1" value="">

Tên Expression và Collections

Ví dụ, một model chứa array of Colors:

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

    public int Age { get; set; }
}

Action method:

csharp
public IActionResult Edit(int id, int colorIndex)
{
    ViewData["Index"] = colorIndex;
    return View(GetPerson(id));
}

Razor sau cho thấy cách bạn 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 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>

foreach nên được sử dụng khi có thể khi giá trị sẽ được sử dụng trong ngữ cảnh 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; tuy nhiên, đánh giá một indexer trong LINQ expression có thể tốn kém và nên được giảm thiểu.

Textarea Tag Helper

Textarea Tag Helper tương tự như Input Tag Helper.

Ví dụ:

csharp
using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public class DescriptionViewModel
    {
        [MinLength(5)]
        [MaxLength(1024)]
        public string Description { get; set; }
    }
}
cshtml
@model DescriptionViewModel

<form asp-controller="Demo" asp-action="RegisterTextArea" method="post">
    <textarea asp-for="Description"></textarea>
    <button type="submit">Test</button>
</form>

HTML sau được tạo ra:

html
<form method="post" action="/Demo/RegisterTextArea">
  <textarea data-val="true"
   data-val-maxlength="The field Description must be a string or array type with a maximum length of &#x27;1024&#x27;."
   data-val-maxlength-max="1024"
   data-val-minlength="The field Description must be a string or array type with a minimum length of &#x27;5&#x27;."
   data-val-minlength-min="5"
   id="Description" name="Description">
  </textarea>
  <button type="submit">Test</button>
  <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
</form>

Label Tag Helper

Label Tag Helper cung cấp các lợi ích sau so với phần tử label HTML thuần:

Ví dụ:

csharp
using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public class SimpleViewModel
    {
        [Required]
        [EmailAddress]
        [Display(Name = "Email Address")]
        public string Email { get; set; }
    }
}
cshtml
@model SimpleViewModel

<form asp-controller="Demo" asp-action="RegisterLabel" method="post">
    <label asp-for="Email"></label>
    <input asp-for="Email" /> <br />
</form>

HTML sau được tạo cho phần tử <label>:

html
<label for="Email">Email Address</label>

Label Tag Helper tạo giá trị thuộc tính for là "Email", đây là ID liên kết với phần tử <input>. Tag Helpers tạo ra các phần tử idfor nhất quán để chúng có thể được liên kết đúng cách. Caption trong ví dụ này đến từ thuộc tính Display. Nếu model không chứa thuộc tính Display, caption sẽ là tên thuộc tính của expression.

Validation Tag Helpers

Có hai Validation Tag Helpers. Validation Message Tag Helper (hiển thị thông báo xác thực cho một thuộc tính đơn trong model), và Validation Summary Tag Helper (hiển thị tóm tắt các lỗi xác thực). Input Tag Helper thêm các thuộc tính xác thực client-side HTML5 vào input elements dựa trên các thuộc tính data annotation trên model classes. Xác thực cũng được thực hiện trên server. Validation Tag Helper hiển thị các thông báo lỗi này khi xảy ra lỗi xác thực.

Validation Message Tag Helper

Validation Message Tag Helper được sử dụng với thuộc tính asp-validation-for trên phần tử HTML span.

cshtml
<span asp-validation-for="Email"></span>

Validation Message Tag Helper sẽ tạo HTML sau:

html
<span class="field-validation-valid"
  data-valmsg-for="Email"
  data-valmsg-replace="true"></span>

Bạn thường sử dụng Validation Message Tag Helper sau Input Tag Helper cho cùng một thuộc tính. Làm như vậy hiển thị bất kỳ thông báo lỗi xác thực nào gần input đã gây ra lỗi.

Bạn phải có view với các tham chiếu script JavaScript và jQuery chính xác cho xác thực client-side. Xem Model Validation để biết thêm thông tin.

Khi xảy ra lỗi xác thực server-side, MVC đặt thông báo lỗi đó làm nội dung của phần tử <span>.

html
<span class="field-validation-error" data-valmsg-for="Email"
            data-valmsg-replace="true">
   The Email Address field is required.
</span>

Validation Summary Tag Helper

Validation Summary Tag Helper được sử dụng để hiển thị tóm tắt thông báo xác thực. Giá trị thuộc tính asp-validation-summary có thể là một trong các giá trị sau:

asp-validation-summaryThông báo xác thực được hiển thị
AllCấp độ thuộc tính và model
ModelOnlyChỉ model
NoneKhông có

Ví dụ

Trong ví dụ sau, data model có các thuộc tính DataAnnotation, tạo thông báo lỗi xác thực trên phần tử <input>. Khi xảy ra lỗi xác thực, Validation Tag Helper hiển thị thông báo lỗi:

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="RegisterValidation" method="post">
    <div asp-validation-summary="ModelOnly"></div>
    <label>Email: <input asp-for="Email" /></label> <br />
    <span asp-validation-for="Email"></span><br />
    <label>Password: <input asp-for="Password" /></label><br />
    <span asp-validation-for="Password"></span><br />
    <button type="submit">Register</button>
</form>

HTML được tạo ra (khi model hợp lệ):

html
<form action="/DemoReg/Register" method="post">
  <label>Email: <input name="Email" id="Email" type="email" value=""
   data-val-required="The Email field is required."
   data-val-email="The Email field is not a valid email address."
   data-val="true"></label><br>
  <span class="field-validation-valid" data-valmsg-replace="true"
   data-valmsg-for="Email"></span><br>
  <label>Password: <input name="Password" id="Password" type="password"
   data-val-required="The Password field is required." data-val="true"></label><br>
  <span class="field-validation-valid" data-valmsg-replace="true"
   data-valmsg-for="Password"></span><br>
  <button type="submit">Register</button>
  <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
</form>

Select Tag Helper

Select Tag Helper asp-for chỉ định tên thuộc tính model cho phần tử selectasp-items chỉ định các phần tử option. Ví dụ:

cshtml
<select asp-for="Country" asp-items="Model.Countries"></select>

Ví dụ:

csharp
using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace FormsTagHelper.ViewModels
{
    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" },
        };
    }
}

Method Index khởi tạo CountryViewModel, đặt quốc gia được chọn, và truyền nó đến view Index.

csharp
public IActionResult Index()
{
    var model = new CountryViewModel();
    model.Country = "CA";
    return View(model);
}

Method Index HTTP POST hiển thị lựa chọn:

csharp
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(CountryViewModel model)
{
    if (ModelState.IsValid)
    {
        var msg = model.Country + " selected";
        return RedirectToAction("IndexSuccess", new { message = msg });
    }

    // If we got this far, something failed; redisplay form.
    return View(model);
}

View Index:

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

Tạo ra HTML sau (với "CA" được chọn):

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>

Chúng tôi không khuyến nghị sử dụng ViewBag hoặc ViewData với Select Tag Helper. View model mạnh mẽ hơn trong việc cung cấp MVC metadata và ít gây rắc rối hơn.

Enum binding (Liên kết enum)

Thường tiện lợi khi sử dụng <select> với thuộc tính enum và tạo các phần tử SelectListItem từ các giá trị enum.

Ví dụ:

csharp
public class CountryEnumViewModel
{
    public CountryEnum EnumCountry { get; set; }
}
csharp
using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public enum CountryEnum
    {
        [Display(Name = "United Mexican States")]
        Mexico,
        [Display(Name = "United States of America")]
        USA,
        Canada,
        France,
        Germany,
        Spain
    }
}

Method GetEnumSelectList tạo một đối tượng SelectList cho enum.

cshtml
@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 sau được tạo ra:

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">United Mexican States</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>

Option Group

Phần tử HTML <optgroup> được tạo khi view model chứa một hoặc nhiều đối tượng SelectListGroup.

CountryViewModelGroup nhóm các phần tử SelectListItem vào các nhóm "North America" và "Europe":

csharp
public class CountryViewModelGroup
{
    public CountryViewModelGroup()
    {
        var NorthAmericaGroup = new SelectListGroup { Name = "North America" };
        var EuropeGroup = new SelectListGroup { Name = "Europe" };

        Countries = new List<SelectListItem>
        {
            new SelectListItem
            {
                Value = "MEX",
                Text = "Mexico",
                Group = NorthAmericaGroup
            },
            new SelectListItem
            {
                Value = "CAN",
                Text = "Canada",
                Group = NorthAmericaGroup
            },
            new SelectListItem
            {
                Value = "US",
                Text = "USA",
                Group = NorthAmericaGroup
            },
            new SelectListItem
            {
                Value = "FR",
                Text = "France",
                Group = EuropeGroup
            },
            new SelectListItem
            {
                Value = "ES",
                Text = "Spain",
                Group = EuropeGroup
            },
            new SelectListItem
            {
                Value = "DE",
                Text = "Germany",
                Group = EuropeGroup
            }
      };
    }

    public string Country { get; set; }

    public List<SelectListItem> Countries { get; }
}

HTML được tạo ra:

html
 <form method="post" action="/Home/IndexGroup">
      <select id="Country" name="Country">
          <optgroup label="North America">
              <option value="MEX">Mexico</option>
              <option value="CAN">Canada</option>
              <option value="US">USA</option>
          </optgroup>
          <optgroup label="Europe">
              <option value="FR">France</option>
              <option value="ES">Spain</option>
              <option value="DE">Germany</option>
          </optgroup>
      </select>
      <br /><button type="submit">Register</button>
      <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
 </form>

Multiple select (Chọn nhiều)

Select Tag Helper sẽ tự động tạo thuộc tính multiple="multiple" nếu thuộc tính được chỉ định trong asp-forIEnumerable. Ví dụ, với model sau:

csharp
using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace FormsTagHelper.ViewModels
{
    public class CountryViewModelIEnumerable
    {
        public IEnumerable<string> CountryCodes { 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"    },
            new SelectListItem { Value = "FR", Text = "France" },
            new SelectListItem { Value = "ES", Text = "Spain"  },
            new SelectListItem { Value = "DE", Text = "Germany"}
         };
    }
}

Với view sau:

cshtml
@model CountryViewModelIEnumerable

<form asp-controller="Home" asp-action="IndexMultiSelect" method="post">
    <select asp-for="CountryCodes" asp-items="Model.Countries"></select> 
    <br /><button type="submit">Register</button>
</form>

Tạo ra HTML sau:

html
<form method="post" action="/Home/IndexMultiSelect">
    <select id="CountryCodes"
    multiple="multiple"
    name="CountryCodes"><option value="MX">Mexico</option>
<option value="CA">Canada</option>
<option value="US">USA</option>
<option value="FR">France</option>
<option value="ES">Spain</option>
<option value="DE">Germany</option>
</select>
    <br /><button type="submit">Register</button>
  <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
</form>

Không có lựa chọn

Nếu bạn thấy mình dùng tùy chọn "not specified" trên nhiều trang, bạn có thể tạo template để tránh lặp lại HTML:

cshtml
@model CountryViewModel

<form asp-controller="Home" asp-action="IndexEmpty" method="post">
    @Html.EditorForModel()
    <br /><button type="submit">Register</button>
</form>

Template Views/Shared/EditorTemplates/CountryViewModel.cshtml:

cshtml
@model CountryViewModel

<select asp-for="Country" asp-items="Model.Countries">
    <option value="">--none--</option>
</select>

Phần tử <option> được chọn đúng (chứa thuộc tính selected="selected") tùy thuộc vào giá trị Country hiện tại.

html
 <form method="post" action="/Home/IndexEmpty">
      <select id="Country" name="Country">
          <option value="">&lt;none&gt;</option>
          <option value="MX">Mexico</option>
          <option value="CA" selected="selected">Canada</option>
          <option value="US">USA</option>
      </select>
      <br /><button type="submit">Register</button>
   <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
 </form>