ASP.NET Core Blazor 輸入元件

注意

這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前版本,請參閱本文的 .NET 8 版本

本文說明 Blazor 的內建輸入元件。

輸入元件

Blazor 架構提供內建的輸入元件來接收和驗證使用者輸入。 下表中的內建輸入元件在 EditForm 中使用 EditContext 支援。

資料表中的元件也在 Razor 元件標記的表單外部支援。 輸入會在變更時以及提交表單時進行驗證。

輸入元件 轉譯為...
InputCheckbox <input type="checkbox">
InputDate<TValue> <input type="date">
InputFile <input type="file">
InputNumber<TValue> <input type="number">
InputRadio<TValue> <input type="radio">
InputRadioGroup<TValue> InputRadio<TValue> 的群組
InputSelect<TValue> <select>
InputText <input>
InputTextArea <textarea>

如需 InputFile 元件的詳細資訊,請參閱 ASP.NET Core Blazor 檔案上傳

輸入元件 轉譯為...
InputCheckbox <input type="checkbox">
InputDate<TValue> <input type="date">
InputNumber<TValue> <input type="number">
InputSelect<TValue> <select>
InputText <input>
InputTextArea <textarea>

注意

InputRadio<TValue>InputRadioGroup<TValue> 元件可在 ASP.NET Core 5.0 或更新版本中取得。 如需詳細資訊,請選取本文的 5.0 或更新版本。

所有輸入元件,包括 EditForm,皆支援任意屬性。 不符合元件參數的任何屬性,會新增至轉譯的 HTML 元素。

輸入元件提供在欄位變更時用於驗證的預設行為:

  • 針對具有 EditContext 的表單中的輸入元件,預設的驗證行為包括更新欄位 CSS 類別,以反映欄位的狀態為有效或無效,以及基礎 HTML 元素的驗證樣式。
  • 針對沒有 EditContext 的控制項,預設驗證會反映有效或無效的狀態,但不會提供基礎 HTML 元素的驗證樣式。

某些元件包含實用的剖析邏輯。 例如,InputDate<TValue>InputNumber<TValue> 會藉由將無法剖析的值註冊為驗證錯誤,以正常處理無法剖析的值。 可以接受 Null 值的型別也支援目標欄位的可為 Null 性 (例如 int? 表示可為 Null 的整數)。

如需 InputFile 元件的詳細資訊,請參閱 ASP.NET Core Blazor 檔案上傳

範例表單

下列 Starship 類型使用於本文的數個範例和其他表單節點文章的範例中,定義了一組含有資料註釋的多種屬性:

  • Id 是必要項目,因為它已加上 RequiredAttribute 註釋。 使用 StringLengthAttributeId 需要至少一個字元但不超過 16 個字元的值。
  • Description 是選用項目,因為它未加上 RequiredAttribute 註釋。
  • Classification 是必要的。
  • MaximumAccommodation 屬性預設為零,但其每個 RangeAttribute 需要一到 100,000 的值。
  • IsValidatedDesign 要求屬性具有 true 值,當屬性繫結至 UI 中的核取方塊時 (<input type="checkbox">),其會比對選取的狀態。
  • ProductionDateDateTime 且為必要項目。

Starship.cs

using System.ComponentModel.DataAnnotations;

namespace BlazorSample;

public class Starship
{
    [Required]
    [StringLength(16, ErrorMessage = "Identifier too long (16 character limit).")]
    public string? Id { get; set; }

    public string? Description { get; set; }

    [Required]
    public string? Classification { get; set; }

    [Range(1, 100000, ErrorMessage = "Accommodation invalid (1-100000).")]
    public int MaximumAccommodation { get; set; }

    [Required]
    [Range(typeof(bool), "true", "true", ErrorMessage = "Approval required.")]
    public bool IsValidatedDesign { get; set; }

    [Required]
    public DateTime ProductionDate { get; set; }
}
using System.ComponentModel.DataAnnotations;

public class Starship
{
    [Required]
    [StringLength(16, ErrorMessage = "Identifier too long (16 character limit).")]
    public string? Id { get; set; }

    public string? Description { get; set; }

    [Required]
    public string? Classification { get; set; }

    [Range(1, 100000, ErrorMessage = "Accommodation invalid (1-100000).")]
    public int MaximumAccommodation { get; set; }

    [Required]
    [Range(typeof(bool), "true", "true", 
        ErrorMessage = "This form disallows unapproved ships.")]
    public bool IsValidatedDesign { get; set; }

    [Required]
    public DateTime ProductionDate { get; set; }
}
using System.ComponentModel.DataAnnotations;

public class Starship
{
    [Required]
    [StringLength(16, ErrorMessage = "Identifier too long (16 character limit).")]
    public string? Id { get; set; }

    public string? Description { get; set; }

    [Required]
    public string? Classification { get; set; }

    [Range(1, 100000, ErrorMessage = "Accommodation invalid (1-100000).")]
    public int MaximumAccommodation { get; set; }

    [Required]
    [Range(typeof(bool), "true", "true", 
        ErrorMessage = "This form disallows unapproved ships.")]
    public bool IsValidatedDesign { get; set; }

    [Required]
    public DateTime ProductionDate { get; set; }
}
using System;
using System.ComponentModel.DataAnnotations;

public class Starship
{
    [Required]
    [StringLength(16, ErrorMessage = "Identifier too long (16 character limit).")]
    public string Id { get; set; }

    public string Description { get; set; }

    [Required]
    public string Classification { get; set; }

    [Range(1, 100000, ErrorMessage = "Accommodation invalid (1-100000).")]
    public int MaximumAccommodation { get; set; }

    [Required]
    [Range(typeof(bool), "true", "true", 
        ErrorMessage = "This form disallows unapproved ships.")]
    public bool IsValidatedDesign { get; set; }

    [Required]
    public DateTime ProductionDate { get; set; }
}
using System;
using System.ComponentModel.DataAnnotations;

public class Starship
{
    [Required]
    [StringLength(16, ErrorMessage = "Identifier too long (16 character limit).")]
    public string Id { get; set; }

    public string Description { get; set; }

    [Required]
    public string Classification { get; set; }

    [Range(1, 100000, ErrorMessage = "Accommodation invalid (1-100000).")]
    public int MaximumAccommodation { get; set; }

    [Required]
    [Range(typeof(bool), "true", "true", 
        ErrorMessage = "This form disallows unapproved ships.")]
    public bool IsValidatedDesign { get; set; }

    [Required]
    public DateTime ProductionDate { get; set; }
}

下列表單會接受並驗證使用者輸入,使用:

  • 上述 Starship 模型中定義的屬性和驗證。
  • Blazor 的數個內建輸入元件。

設定了船舶分類 (Classification) 的模型屬性時,會核取符合模型的選項。 例如,探勘船的分類為 checked="@(Model!.Classification == "Exploration")"。 之所以明確設定核取的選項,是因為 <select> 元素的值只存在於瀏覽器中。 如果表單於提交後在伺服器上轉譯,則用戶端的任何狀態都會覆寫為伺服器的狀態,這通常不會使選項標示為已核取。 藉由從模型屬性設定核取的選項,分類一律會反映模型的狀態。 這會保留表單提交時的分類選取,而導致表單在伺服器上重新轉譯。 若表單未在伺服器上重新轉譯 (例如,直接對元件套用互動式伺服器轉譯模式時),不需要從模型明確指派核取的選項,因為 Blazor 會保留 <select> 元素在用戶端上的狀態。

Starship3.razor

@page "/starship-3"
@inject ILogger<Starship3> Logger

<h1>Starfleet Starship Database</h1>

<h2>New Ship Entry Form</h2>

<EditForm Model="Model" OnValidSubmit="Submit" FormName="Starship3">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <div>
        <label>
            Identifier: 
            <InputText @bind-Value="Model!.Id" />
        </label>
    </div>
    <div>
        <label>
            Description (optional): 
            <InputTextArea @bind-Value="Model!.Description" />
        </label>
    </div>
    <div>
        <label>
            Primary Classification: 
            <InputSelect @bind-Value="Model!.Classification">
                <option value="">
                    Select classification ...
                </option>
                <option checked="@(Model!.Classification == "Exploration")" 
                    value="Exploration">
                    Exploration
                </option>
                <option checked="@(Model!.Classification == "Diplomacy")" 
                    value="Diplomacy">
                    Diplomacy
                </option>
                <option checked="@(Model!.Classification == "Defense")" 
                    value="Defense">
                    Defense
                </option>
            </InputSelect>
        </label>
    </div>
    <div>
        <label>
            Maximum Accommodation: 
            <InputNumber @bind-Value="Model!.MaximumAccommodation" />
        </label>
    </div>
    <div>
        <label>
            Engineering Approval: 
            <InputCheckbox @bind-Value="Model!.IsValidatedDesign" />
        </label>
    </div>
    <div>
        <label>
            Production Date: 
            <InputDate @bind-Value="Model!.ProductionDate" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

@code {
    [SupplyParameterFromForm]
    private Starship? Model { get; set; }

    protected override void OnInitialized() =>
        Model ??= new() { ProductionDate = DateTime.UtcNow };

    private void Submit()
    {
        Logger.LogInformation("Id = {Id} Description = {Description} " +
            "Classification = {Classification} MaximumAccommodation = " +
            "{MaximumAccommodation} IsValidatedDesign = " +
            "{IsValidatedDesign} ProductionDate = {ProductionDate}",
            Model?.Id, Model?.Description, Model?.Classification,
            Model?.MaximumAccommodation, Model?.IsValidatedDesign,
            Model?.ProductionDate);
    }
}
@page "/starship-3"
@inject ILogger<Starship3> Logger

<h1>Starfleet Starship Database</h1>

<h2>New Ship Entry Form</h2>

<EditForm Model="Model" OnValidSubmit="Submit">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <div>
        <label>
            Identifier:
            <InputText @bind-Value="Model!.Id" />
        </label>
    </div>
    <div>
        <label>
            Description (optional):
            <InputTextArea @bind-Value="Model!.Description" />
        </label>
    </div>
    <div>
        <label>
            Primary Classification:
            <InputSelect @bind-Value="Model!.Classification">
                <option value="">Select classification ...</option>
                <option value="Exploration">Exploration</option>
                <option value="Diplomacy">Diplomacy</option>
                <option value="Defense">Defense</option>
            </InputSelect>
        </label>
    </div>
    <div>
        <label>
            Maximum Accommodation:
            <InputNumber @bind-Value="Model!.MaximumAccommodation" />
        </label>
    </div>
    <div>
        <label>
            Engineering Approval:
            <InputCheckbox @bind-Value="Model!.IsValidatedDesign" />
        </label>
    </div>
    <div>
        <label>
            Production Date:
            <InputDate @bind-Value="Model!.ProductionDate" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

@code {
    private Starship? Model { get; set; }

    protected override void OnInitialized() =>
        Model ??= new() { ProductionDate = DateTime.UtcNow };

    private void Submit()
    {
        Logger.LogInformation("Id = {Id} Description = {Description} " +
            "Classification = {Classification} MaximumAccommodation = " +
            "{MaximumAccommodation} IsValidatedDesign = " +
            "{IsValidatedDesign} ProductionDate = {ProductionDate}", 
            Model?.Id, Model?.Description, Model?.Classification, 
            Model?.MaximumAccommodation, Model?.IsValidatedDesign, 
            Model?.ProductionDate);
    }
}

上述範例中的 EditForm 會根據指派的 Starship 執行個體 (Model="...") 建立 EditContext,並處理有效的表單。 下一個範例示範如何將 EditContext 指派給表單,並在提交表單時進行驗證。

在以下範例中:

  • 使用了舊版 Starfleet Starship Database 表單 (Starship3 元件) 的縮減版本,其只接受星際飛船識別碼的值。建立 Starship 型別的執行個體時,其他 Starship 屬性會收到有效的預設值。
  • Submit 方法會在選取 Submit 按鈕時執行。
  • 透過在 Submit 方法中呼叫 EditContext.Validate 來驗證表單。
  • 根據驗證結果執行記錄。

注意

下一個範例中的 Submit,會以非同步方法示範,因為儲存表單值通常會使用非同步呼叫 (await ...)。 如果表單用在所示的測試應用程式中,則 Submit 只會以同步方式執行。 針對測試目的,請忽略下列建置警告:

此非同步方法缺少 'await' 運算子,而且會以同步方式執行。 ...

Starship4.razor

@page "/starship-4"
@inject ILogger<Starship4> Logger

<EditForm EditContext="editContext" OnSubmit="Submit" FormName="Starship4">
    <DataAnnotationsValidator />
    <div>
        <label>
            Identifier: 
            <InputText @bind-Value="Model!.Id" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

@code {
    private EditContext? editContext;

    [SupplyParameterFromForm]
    private Starship? Model { get; set; }

    protected override void OnInitialized()
    {
        Model ??=
            new()
                {
                    Id = "NCC-1701",
                    Classification = "Exploration",
                    MaximumAccommodation = 150,
                    IsValidatedDesign = true,
                    ProductionDate = new DateTime(2245, 4, 11)
                };
        editContext = new(Model);
    }

    private async Task Submit()
    {
        if (editContext != null && editContext.Validate())
        {
            Logger.LogInformation("Submit called: Form is valid");

            // await ...
        }
        else
        {
            Logger.LogInformation("Submit called: Form is INVALID");
        }
    }
}
@page "/starship-4"
@inject ILogger<Starship4> Logger

<EditForm EditContext="editContext" OnSubmit="Submit">
    <DataAnnotationsValidator />
    <div>
        <label>
            Identifier:
            <InputText @bind-Value="Model!.Id" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

@code {
    private EditContext? editContext;

    private Starship Model { get; set; }

    protected override void OnInitialized()
    {
        Model ??= 
            new()
            {
                Id = "NCC-1701",
                Classification = "Exploration",
                MaximumAccommodation = 150,
                IsValidatedDesign = true,
                ProductionDate = new DateTime(2245, 4, 11)
            };
        editContext = new(Model);
    }

    private async Task Submit()
    {
        if (editContext != null && editContext.Validate())
        {
            Logger.LogInformation("Submit called: Form is valid");

            // await ...
        }
        else
        {
            Logger.LogInformation("Submit called: Form is INVALID");
        }
    }
}

注意

支援在指派之後變更 EditContext

使用 InputSelect 元件的多個選項選取項目

繫結支援使用 InputSelect<TValue> 元件進行 multiple 選項選取項目。 @onchange 事件會透過事件引數 (ChangeEventArgs) 提供所選選項的陣列。 值必須繫結至陣列類型,而且繫結至陣列類型會使得 multiple 屬性成為 InputSelect<TValue> 標記上的選用屬性。

在下列範例中,使用者必須至少選取兩個星際飛船分類,但不超過三個分類。

Starship5.razor

@page "/starship-5"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship5> Logger

<h1>Bind Multiple <code>InputSelect</code> Example</h1>

<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="Starship5">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <div>
        <label>
            Select classifications (Minimum: 2, Maximum: 3):
            <InputSelect @bind-Value="Model!.SelectedClassification">
                <option value="@Classification.Exploration">Exploration</option>
                <option value="@Classification.Diplomacy">Diplomacy</option>
                <option value="@Classification.Defense">Defense</option>
                <option value="@Classification.Research">Research</option>
            </InputSelect>
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

@if (Model?.SelectedClassification?.Length > 0)
{
    <div>@string.Join(", ", Model.SelectedClassification)</div>
}

@code {
    private EditContext? editContext;

    [SupplyParameterFromForm]
    private Starship? Model { get; set; }

    protected override void OnInitialized()
    {
        Model = new();
        editContext = new(Model);
    }

    private void Submit()
    {
        Logger.LogInformation("Submit called: Processing the form");
    }

    private class Starship
    {
        [Required]
        [MinLength(2, ErrorMessage = "Select at least two classifications.")]
        [MaxLength(3, ErrorMessage = "Select no more than three classifications.")]
        public Classification[]? SelectedClassification { get; set; } =
            new[] { Classification.None };
    }

    private enum Classification { None, Exploration, Diplomacy, Defense, Research }
}
@page "/starship-5"
@using System.ComponentModel.DataAnnotations
@inject ILogger<Starship5> Logger

<h1>Bind Multiple <code>InputSelect</code> Example</h1>

<EditForm EditContext="editContext" OnValidSubmit="Submit">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <div>
        <label>
            Select classifications (Minimum: 2, Maximum: 3):
            <InputSelect @bind-Value="Model!.SelectedClassification">
                <option value="@Classification.Exploration">Exploration</option>
                <option value="@Classification.Diplomacy">Diplomacy</option>
                <option value="@Classification.Defense">Defense</option>
                <option value="@Classification.Research">Research</option>
            </InputSelect>
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

@if (Model?.SelectedClassification?.Length > 0)
{
    <div>@string.Join(", ", Model.SelectedClassification)</div>
}

@code {
    private EditContext? editContext;

    private Starship? Model { get; set; }

    protected override void OnInitialized()
    {
        Model ??= new();
        editContext = new(Model);
    }

    private void Submit()
    {
        Logger.LogInformation("Submit called: Processing the form");
    }

    private class Starship
    {
        [Required]
        [MinLength(2, ErrorMessage = "Select at least two classifications.")]
        [MaxLength(3, ErrorMessage = "Select no more than three classifications.")]
        public Classification[]? SelectedClassification { get; set; } =
            new[] { Classification.None };
    }

    private enum Classification { None, Exploration, Diplomacy, Defense, Research }
}

如需資料繫結中如何處理空白字串和 null 值的相關資訊,請參閱InputSelect 選項繫結至 C# 物件 null一節。

InputSelect 選項繫結至 C# 物件 null

如需資料繫結中如何處理空白字串和 null 值的相關資訊,請參閱 ASP.NET Core Blazor 資料繫結

顯示名稱支援

數個內建元件支援使用 InputBase<TValue>.DisplayName 參數顯示名稱。

範例表單小節的 Starfleet Starship Database 表單 (Starship3 元件) 中,新星際飛船的生產日期未指定顯示名稱:

<label>
    Production Date:
    <InputDate @bind-Value="Model!.ProductionDate" />
</label>

如果欄位在提交表單時包含無效的日期,則錯誤訊息不會顯示易記名稱。 欄位名稱 "ProductionDate" 在驗證摘要中出現時,於 "Production" 與 "Date" 之間沒有空格:

ProductionDate 欄位必須是日期。

DisplayName 屬性設定為易記名稱,並於字組 "Production" 與 "Date" 之間加上空格:

<label>
    Production Date:
    <InputDate @bind-Value="Model!.ProductionDate" 
        DisplayName="Production Date" />
</label>

當欄位的值無效,驗證摘要會顯示易記名稱:

Production Date 欄位必須是日期。

錯誤訊息範本支援

InputDate<TValue>InputNumber<TValue> 支援錯誤訊息範本:

在已指派易記顯示名稱範例表單小節的 Starfleet Starship Database 表單 (Starship3 元件) 中,Production Date 欄位會使用下列預設錯誤訊息範本產生錯誤訊息:

The {0} field must be a date.

{0} 預留位置的位置是向使用者顯示錯誤時,DisplayName 屬性的值出現的位置。

<label>
    Production Date:
    <InputDate @bind-Value="Model!.ProductionDate" 
        DisplayName="Production Date" />
</label>

Production Date 欄位必須是日期。

將自訂範本指派給 ParsingErrorMessage,以提供自訂訊息:

<label>
    Production Date:
    <InputDate @bind-Value="Model!.ProductionDate" 
        DisplayName="Production Date" 
        ParsingErrorMessage="The {0} field has an incorrect date value." />
</label>

Production Date 欄位的日期值不正確。

範例表單小節的 Starfleet Starship Database 表單 (Starship3 元件) 中使用預設的錯誤訊息範本:

The {0} field must be a date.

{0} 預留位置的位置是向使用者顯示錯誤時,DisplayName 屬性的值出現的位置。

<label>
    Production Date:
    <InputDate @bind-Value="Model!.ProductionDate" />
</label>

ProductionDate 欄位必須是日期。

將自訂範本指派給 ParsingErrorMessage,以提供自訂訊息:

<label>
    Production Date:
    <InputDate @bind-Value="Model!.ProductionDate" 
        ParsingErrorMessage="The {0} field has an incorrect date value." />
</label>

ProductionDate 欄位的日期值不正確。