Componentes de entrada do ASP.NET Core Blazor

Observação

Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Este artigo descreve os componentes de entrada internos do Blazor.

Componentes de entrada

A estrutura Blazor fornece componentes de entrada internos para receber e validar a entrada do usuário. Os componentes de entrada internos na tabela a seguir têm suporte em um EditForm com um EditContext.

Os componentes na tabela também têm suporte fora de um formulário na marcação do componente Razor. As entradas são validadas quando são alteradas e quando um formulário é enviado.

Componente de entrada Renderizado como...
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> Grupo de InputRadio<TValue> filho
InputSelect<TValue> <select>
InputText <input>
InputTextArea <textarea>

Para obter mais informações sobre o componente InputFile, confira Uploads de arquivo Blazor do ASP.NET Core.

Componente de entrada Renderizado como...
InputCheckbox <input type="checkbox">
InputDate<TValue> <input type="date">
InputNumber<TValue> <input type="number">
InputSelect<TValue> <select>
InputText <input>
InputTextArea <textarea>

Observação

Os componentes InputRadio<TValue> e InputRadioGroup<TValue> estão disponíveis no ASP.NET Core 5.0 ou posterior. Para obter mais informações, selecione uma versão 5.0 ou posterior deste artigo.

Todos os componentes de entrada, incluindo EditForm, dão suporte a atributos arbitrários. Todo atributo que não corresponda a um parâmetro de componente é adicionado ao elemento HTML renderizado.

Os componentes de entrada fornecem o comportamento padrão para validação quando um campo é alterado:

  • Para componentes de entrada em um formulário com um EditContext, o comportamento de validação padrão inclui a atualização da classe CSS do campo para refletir o estado do campo como válido ou inválido com o estilo de validação do elemento HTML subjacente.
  • Para controles que não têm um EditContext, a validação padrão reflete o estado válido ou inválido, mas não fornece estilo de validação para o elemento HTML subjacente.

Alguns componentes incluem lógica de análise útil. Por exemplo, InputDate<TValue> e InputNumber<TValue> manipulam valores não analisáveis graciosamente registrando valores não analisáveis como erros de validação. Os tipos que podem aceitar valores nulos também dão suporte à anulabilidade do campo de destino (por exemplo, int? para um inteiro anulável).

Para obter mais informações sobre o componente InputFile, confira Uploads de arquivo Blazor do ASP.NET Core.

Formulário de exemplo

O tipo Starship a seguir, que é usado em vários exemplos deste artigo e em outros artigos de nó do Forms, define um conjunto diversificado de propriedades com anotações de dados:

  • Id é obrigatório porque é anotado com o RequiredAttribute. Id requer um valor de pelo menos um caractere, mas não mais do que 16 caracteres, usando o StringLengthAttribute.
  • Description é opcional porque não é anotado com o RequiredAttribute.
  • Classification é obrigatório.
  • A propriedade MaximumAccommodation assume o padrão de zero, mas requer um valor de um a 100.000 para cada RangeAttribute.
  • IsValidatedDesign requer que a propriedade tenha um valor true, que corresponde a um estado selecionado quando a propriedade está associada a uma caixa de seleção na interface do usuário (<input type="checkbox">).
  • ProductionDate é um DateTime e é obrigatório.

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; }
}

O formulário abaixo aceita e valida a entrada do usuário usando:

  • As propriedades e a validação definidas no modelo anterior Starship.
  • Vários componentes de entrada internos do Blazor.

Quando a propriedade do modelo para a classificação do navio (Classification) é definida, a opção correspondente ao modelo é marcada. Por exemplo, checked="@(Model!.Classification == "Exploration")" para a classificação de um navio de exploração. A razão para definir explicitamente a opção marcada é que o valor de um elemento <select> só está presente no navegador. Se o formulário for renderizado no servidor depois de enviado, qualquer estado do cliente será substituído pelo estado do servidor, que normalmente não deixa uma opção marcada. Ao definir a opção marcada da propriedade do modelo, a classificação sempre reflete o estado do modelo. Isso preserva a seleção de classificação nos envios de formulário que resultam em uma nova renderização do formulário no servidor. Em situações em que o formulário não é renderizado novamente no servidor, por exemplo, quando o modo de renderização do Servidor Interativo é aplicado diretamente ao componente, a atribuição explícita da opção marcada no modelo não é necessária, porque Blazor preserva o estado do elemento <select> no cliente.

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);
    }
}

O EditForm no exemplo anterior cria um EditContext baseado na instância de Starship atribuída (Model="...") e manipula um formulário válido. O próximo exemplo demonstra como atribuir um EditContext a um formulário e validar quando o formulário é enviado.

No exemplo a seguir:

  • Uma versão abreviada do formulário Starfleet Starship Database anterior (componente Starship3) é usada com a aceitação de apenas um valor para o identificador da nave estelar. As outras propriedades Starship recebem valores padrão válidos quando uma instância do tipo Starship é criada.
  • O método Submit é executado quando o botão Submit é selecionado.
  • O formulário é validado com o chamamento de EditContext.Validate no método Submit.
  • O registro em log é executado dependendo do resultado da validação.

Observação

Submit no próximo exemplo é demonstrado como um método assíncrono porque o armazenamento de valores de formulário geralmente usa chamadas assíncronas (await ...). Se o formulário for usado em um aplicativo de teste, conforme mostrado, Submit será executado apenas de forma síncrona. Para fins de teste, ignore o seguinte aviso de build:

O método assíncrono não possui operadores 'await' e será executado de forma síncrona. ...

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");
        }
    }
}

Observação

Não há suporte à alteração de EditContext após a atribuição.

Seleção de várias opções com o componente InputSelect

A associação dá suporte à opção multiple com o componente InputSelect<TValue>. O evento @onchange fornece uma matriz das opções selecionadas por meio de argumentos de evento (ChangeEventArgs). O valor precisa ser associado a um tipo de matriz, e a associação a um tipo de matriz torna o atributo multiple opcional na marca InputSelect<TValue>.

No exemplo a seguir, o usuário precisa selecionar pelo menos duas classificações de starship, mas não mais do que três.

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 }
}

Para obter informações sobre como cadeias de caracteres e valores null vazios são tratados na associação de dados, confira a seção Associação de opções InputSelect aos valores null do objeto C#.

Associação de opções InputSelect a valores null de objeto C#

Para obter informações sobre como cadeias de caracteres e valores null vazios são tratados na associação de dados, confira Associação de dados Blazor do ASP.NET Core.

Suporte ao nome de exibição

Vários componentes internos dão suporte a nomes de exibição com o parâmetro InputBase<TValue>.DisplayName.

No formulário Starfleet Starship Database (componente Starship3) da seção Formulário de exemplo, a data de produção de uma novo starship não especifica um nome de exibição:

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

Se o campo contiver uma data inválida quando o formulário for enviado, a mensagem de erro não exibirá um nome amigável. O nome do campo, "ProductionDate", não tem um espaço entre "Production" e "Date" quando aparece no resumo da validação:

O campo ProductionDate precisa ser uma data.

Defina a propriedade DisplayName como um nome amigável com um espaço entre as palavras "Production" e "Date":

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

O resumo da validação exibe o nome amigável quando o valor do campo é inválido:

O campo Data de Produção precisa ser uma data.

Suporte ao modelo de mensagem de erro

InputDate<TValue> e InputNumber<TValue> dão suporte a modelos de mensagem de erro:

No formulário Starfleet Starship Database (componenteStarship3) da seção Formulário de exemplo com um nome de exibição amigável atribuído, o campo Production Date produz uma mensagem de erro usando o seguinte modelo de mensagem de erro padrão:

The {0} field must be a date.

A posição do espaço reservado {0} é onde o valor da propriedade DisplayName aparece quando o erro é exibido para o usuário.

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

O campo Data de Produção precisa ser uma data.

Atribua um modelo personalizado para ParsingErrorMessage a fim de fornecer uma mensagem personalizada:

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

O campo Data de Produção tem um valor de data incorreto.

O formulário Starfleet Starship Database (componenteStarship3) da seção Formulário de exemplo usa um modelo de mensagem de erro padrão:

The {0} field must be a date.

A posição do espaço reservado {0} é onde o valor da propriedade DisplayName aparece quando o erro é exibido para o usuário.

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

O campo ProductionDate precisa ser uma data.

Atribua um modelo personalizado para ParsingErrorMessage a fim de fornecer uma mensagem personalizada:

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

O campo ProductionDate tem um valor de data incorreto.