Partilhar via


ASP.NET Core associação de formulários Blazor

Observação

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.

Advertência

Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.

Importante

Estas informações referem-se a um produto de pré-lançamento que pode ser substancialmente modificado antes de ser lançado comercialmente. A Microsoft não oferece garantias, expressas ou implícitas, em relação às informações fornecidas aqui.

Para a versão atual, consulte a versão .NET 9 deste artigo.

Este artigo explica como usar a vinculação em formulários Blazor.

EditForm / EditContext modelo

Um EditForm cria um EditContext com base no objeto atribuído como um valor em cascata para outros componentes no formulário. O EditContext rastreia metadados sobre o processo de edição, incluindo quais campos de formulário foram modificados e as mensagens de validação atuais. A atribuição a um EditForm.Model ou a um EditForm.EditContext pode vincular um formulário aos dados.

Vinculação de modelo

Atribuição a EditForm.Model:

<EditForm ... Model="Model" ...>
    ...
</EditForm>

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

    protected override void OnInitialized() => Model ??= new();
}
<EditForm ... Model="Model" ...>
    ...
</EditForm>

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

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

Observação

A maioria dos exemplos de modelo de formulário deste artigo vincula formulários a propriedades de C#, mas a vinculação de campo em C# também é suportada.

Vinculação de contexto

Atribuição a EditForm.EditContext:

<EditForm ... EditContext="editContext" ...>
    ...
</EditForm>

@code {
    private EditContext? editContext;

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

    protected override void OnInitialized()
    {
        Model ??= new();
        editContext = new(Model);
    }
}
<EditForm ... EditContext="editContext" ...>
    ...
</EditForm>

@code {
    private EditContext? editContext;

    public Starship? Model { get; set; }

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

Atribua umEditContextou umModel a um EditForm. Se ambos forem atribuídos, ocorre um erro de tempo de execução.

Tipos suportados

Suportes de ligação:

  • Tipos primitivos
  • Coleções
  • Tipos complexos
  • Tipos recursivos
  • Tipos com construtores
  • Enums

Você também pode usar os atributos [DataMember] e [IgnoreDataMember] para personalizar a vinculação do modelo. Use esses atributos para renomear propriedades, ignorar propriedades e marcar propriedades conforme necessário.

Opções de vinculação adicionais

Opções adicionais de vinculação de modelo estão disponíveis a partir de RazorComponentsServiceOptions ao chamar AddRazorComponents:

O seguinte demonstra os valores padrão atribuídos pela estrutura:

builder.Services.AddRazorComponents(options =>
{
    options.FormMappingUseCurrentCulture = true;
    options.MaxFormMappingCollectionSize = 1024;
    options.MaxFormMappingErrorCount = 200;
    options.MaxFormMappingKeySize = 1024 * 2;
    options.MaxFormMappingRecursionDepth = 64;
}).AddInteractiveServerComponents();

Nomes de formulários

Use o parâmetro FormName para atribuir um nome de formulário. Os nomes de formulário devem ser exclusivos para vincular dados de modelo. O seguinte formulário é denominado RomulanAle:

<EditForm ... FormName="RomulanAle" ...>
    ...
</EditForm>

Especificando um nome de formulário:

  • É necessário para todos os formulários que são enviados por componentes do lado do servidor que são renderizados estaticamente.
  • Não é necessário para formulários enviados por componentes renderizados interativamente, o que inclui formulários em aplicativos Blazor WebAssembly e componentes com um modo de renderização interativo. No entanto, recomendamos fornecer um nome de formulário exclusivo para cada formulário para evitar erros de postagem de formulário de tempo de execução se a interatividade for descartada para um formulário.

O nome do formulário só é verificado quando o formulário é postado num endpoint como uma solicitação HTTP POST tradicional de um componente de servidor renderizado de forma estática. A estrutura não lança uma exceção no ponto de renderização de um formulário, mas apenas no ponto em que um HTTP POST chega e não especifica um nome de formulário.

Há um escopo de formulário sem nome (string vazia) acima do componente raiz da aplicação, o que é suficiente quando não há colisões de nomes de formulário na aplicação. Se for possível haver colisões de nome de formulário, como quando incluir um formulário de uma biblioteca e não tiver controlo sobre o nome do formulário usado pelo programador da biblioteca, forneça um escopo de nome de formulário com o componente FormMappingScope no projeto principal do Blazor Web App.

No exemplo a seguir, o componente HelloFormFromLibrary tem um formulário chamado Hello e está em uma biblioteca.

HelloFormFromLibrary.razor:

<EditForm FormName="Hello" Model="this" OnSubmit="Submit">
    <InputText @bind-Value="Name" />
    <button type="submit">Submit</button>
</EditForm>

@if (submitted)
{
    <p>Hello @Name from the library's form!</p>
}

@code {
    bool submitted = false;

    [SupplyParameterFromForm]
    private string? Name { get; set; }

    private void Submit() => submitted = true;
}

O componente NamedFormsWithScope a seguir usa o componente HelloFormFromLibrary da biblioteca e também tem um formulário chamado Hello. O nome do escopo do componente FormMappingScope é ParentContext para todos os formulários fornecidos pelo componente HelloFormFromLibrary. Embora ambos os formulários neste exemplo tenham o nome (Hello), os nomes dos formulários não entram em conflito e os eventos são encaminhados para o formulário correto nos eventos POST do formulário.

NamedFormsWithScope.razor:

@page "/named-forms-with-scope"

<div>Hello form from a library</div>

<FormMappingScope Name="ParentContext">
    <HelloFormFromLibrary />
</FormMappingScope>

<div>Hello form using the same form name</div>

<EditForm FormName="Hello" Model="this" OnSubmit="Submit">
    <InputText @bind-Value="Name" />
    <button type="submit">Submit</button>
</EditForm>

@if (submitted)
{
    <p>Hello @Name from the app form!</p>
}

@code {
    bool submitted = false;

    [SupplyParameterFromForm]
    private string? Name { get; set; }

    private void Submit() => submitted = true;
}

Forneça um parâmetro a partir do formulário ([SupplyParameterFromForm])

O atributo [SupplyParameterFromForm] indica que o valor da propriedade associada deve ser fornecido a partir dos dados do formulário para o formulário. Os dados na solicitação que correspondem ao nome da propriedade são vinculados à propriedade. As entradas baseadas em InputBase<TValue> geram nomes de valor de formulário que correspondem aos nomes que o Blazor usa para ligação de modelo. Ao contrário das propriedades dos parâmetros do componente ([Parameter]), as propriedades anotadas com [SupplyParameterFromForm] não precisam ser marcadas public.

Você pode especificar os seguintes parâmetros de vinculação de formulário para o atributo [SupplyParameterFromForm]:

  • Name: Obtém ou define o nome para o parâmetro. O nome é usado para determinar o prefixo a ser usado para corresponder aos dados do formulário e decidir se o valor precisa ou não ser vinculado.
  • FormName: Obtém ou define o nome do manipulador. O nome é usado para associar o parâmetro ao formulário pelo nome do formulário, para decidir se o valor precisa ou não ser associado.

O exemplo a seguir vincula independentemente dois formulários aos seus modelos pelo nome do formulário.

Starship6.razor:

@page "/starship-6"
@inject ILogger<Starship6> Logger

<EditForm Model="Model1" OnSubmit="Submit1" FormName="Holodeck1">
    <div>
        <label>
            Holodeck 1 Identifier: 
            <InputText @bind-Value="Model1!.Id" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

<EditForm Model="Model2" OnSubmit="Submit2" FormName="Holodeck2">
    <div>
        <label>
            Holodeck 2 Identifier: 
            <InputText @bind-Value="Model2!.Id" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

@code {
    [SupplyParameterFromForm(FormName = "Holodeck1")]
    private Holodeck? Model1 { get; set; }

    [SupplyParameterFromForm(FormName = "Holodeck2")]
    private Holodeck? Model2 { get; set; }

    protected override void OnInitialized()
    {
        Model1 ??= new();
        Model2 ??= new();
    }

    private void Submit1() => Logger.LogInformation("Submit1: Id={Id}", Model1?.Id);

    private void Submit2() => Logger.LogInformation("Submit2: Id={Id}", Model2?.Id);

    public class Holodeck
    {
        public string? Id { get; set; }
    }
}
@page "/starship-6"
@inject ILogger<Starship6> Logger

<EditForm Model="Model1" OnSubmit="Submit1" FormName="Holodeck1">
    <div>
        <label>
            Holodeck 1 Identifier: 
            <InputText @bind-Value="Model1!.Id" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

<EditForm Model="Model2" OnSubmit="Submit2" FormName="Holodeck2">
    <div>
        <label>
            Holodeck 2 Identifier: 
            <InputText @bind-Value="Model2!.Id" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

@code {
    [SupplyParameterFromForm(FormName = "Holodeck1")]
    private Holodeck? Model1 { get; set; }

    [SupplyParameterFromForm(FormName = "Holodeck2")]
    private Holodeck? Model2 { get; set; }

    protected override void OnInitialized()
    {
        Model1 ??= new();
        Model2 ??= new();
    }

    private void Submit1() => Logger.LogInformation("Submit1: Id={Id}", Model1?.Id);

    private void Submit2() => Logger.LogInformation("Submit2: Id={Id}", Model2?.Id);

    public class Holodeck
    {
        public string? Id { get; set; }
    }
}

Aninhar e vincular formulários

As diretrizes a seguir demonstram como aninhar e vincular formulários filho.

A seguinte classe de dados do navio (ShipDetails) contém uma descrição e comprimento para um subformulário.

ShipDetails.cs:

namespace BlazorSample;

public class ShipDetails
{
    public string? Description { get; set; }
    public int? Length { get; set; }
}
namespace BlazorSample;

public class ShipDetails
{
    public string? Description { get; set; }
    public int? Length { get; set; }
}

A classe Ship seguinte designa um identificador (Id) e inclui os dados do navio.

Ship.cs:

namespace BlazorSample
{
    public class Ship
    {
        public string? Id { get; set; }
        public ShipDetails Details { get; set; } = new();
    }
}
namespace BlazorSample
{
    public class Ship
    {
        public string? Id { get; set; }
        public ShipDetails Details { get; set; } = new();
    }
}

O subformulário a seguir é usado para editar valores do tipo ShipDetails. Isso é implementado herdando Editor<T> na parte superior do componente. Editor<T> assegura que o componente filho gere os nomes corretos dos campos do formulário com base no modelo (T), onde T no exemplo a seguir é ShipDetails.

StarshipSubform.razor:

@inherits Editor<ShipDetails>

<div>
    <label>
        Description: 
        <InputText @bind-Value="Value!.Description" />
    </label>
</div>
<div>
    <label>
        Length: 
        <InputNumber @bind-Value="Value!.Length" />
    </label>
</div>
@inherits Editor<ShipDetails>

<div>
    <label>
        Description: 
        <InputText @bind-Value="Value!.Description" />
    </label>
</div>
<div>
    <label>
        Length: 
        <InputNumber @bind-Value="Value!.Length" />
    </label>
</div>

O formulário principal está vinculado à classe Ship. O componente StarshipSubform é usado para editar detalhes do navio, vinculados como Model!.Details.

Starship7.razor:

@page "/starship-7"
@inject ILogger<Starship7> Logger

<EditForm Model="Model" OnSubmit="Submit" FormName="Starship7">
    <div>
        <label>
            Identifier: 
            <InputText @bind-Value="Model!.Id" />
        </label>
    </div>
    <StarshipSubform @bind-Value="Model!.Details" />
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

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

    protected override void OnInitialized() => Model ??= new();

    private void Submit() => 
        Logger.LogInformation("Id = {Id} Desc = {Description} Length = {Length}",
            Model?.Id, Model?.Details?.Description, Model?.Details?.Length);
}
@page "/starship-7"
@inject ILogger<Starship7> Logger

<EditForm Model="Model" OnSubmit="Submit" FormName="Starship7">
    <div>
        <label>
            Identifier: 
            <InputText @bind-Value="Model!.Id" />
        </label>
    </div>
    <StarshipSubform @bind-Value="Model!.Details" />
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

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

    protected override void OnInitialized() => Model ??= new();

    private void Submit() => 
        Logger.LogInformation("Id = {Id} Desc = {Description} Length = {Length}",
            Model?.Id, Model?.Details?.Description, Model?.Details?.Length);
}

Inicializar dados de formulário com SSR estático

Quando um componente adota SSR estático, os métodos de ciclo de vida OnInitialized{Async} e OnParametersSet{Async} são disparados quando o componente é inicialmente renderizado e em cada envio de formulário POST para o servidor. Para inicializar valores de modelo de formulário, confirme se o modelo já tem dados antes de atribuir novos valores de modelo em OnParametersSet{Async}, como demonstra o exemplo a seguir.

StarshipInit.razor:

@page "/starship-init"
@inject ILogger<StarshipInit> Logger

<EditForm Model="Model" OnValidSubmit="Submit" FormName="StarshipInit">
    <div>
        <label>
            Identifier:
            <InputText @bind-Value="Model!.Id" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

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

    protected override void OnInitialized() => Model ??= new();

    protected override void OnParametersSet()
    {
        if (Model!.Id == default)
        {
            LoadData();
        }
    }

    private void LoadData()
    {
        Model!.Id = "Set by LoadData";
    }

    private void Submit()
    {
        Logger.LogInformation("Id = {Id}", Model?.Id);
    }

    public class Starship
    {
        public string? Id { get; set; }
    }
}

Cenários de erro de mapeamento avançado de formulários

A estrutura instancia e preenche o FormMappingContext de um formulário, que é o contexto associado à operação de mapeamento de um determinado formulário. Cada escopo de mapeamento, definido por um componente FormMappingScope, instancia FormMappingContext. Cada vez que um [SupplyParameterFromForm] solicita um valor ao contexto, a estrutura preenche o FormMappingContext com o valor tentado e quaisquer erros de mapeamento.

Não se espera que os desenvolvedores interajam com FormMappingContext diretamente, pois é principalmente uma fonte de dados para InputBase<TValue>, EditContexte outras implementações internas para mostrar erros de mapeamento como erros de validação. Em cenários personalizados avançados, os desenvolvedores podem acessar FormMappingContext diretamente como um [CascadingParameter] para escrever código adaptado que processa os valores tentativos e erros de mapeamento.

Componentes de entrada personalizados

Para cenários de processamento de entrada personalizados, as subseções a seguir demonstram componentes de entrada personalizados:

Recomendamos que você derive seus componentes de entrada personalizados do InputBase<TValue> a menos que requisitos específicos o impeçam de fazê-lo. A classe InputBase<TValue> é mantida ativamente pela equipe ASP.NET Core, garantindo que ela permaneça up-toatualizada com os recursos de Blazor e as alterações de estrutura mais recentes.

Componente de entrada baseado em InputBase<T>

O seguinte componente de exemplo:

  • Herda de InputBase<TValue>. Os componentes que herdam de InputBase<TValue> devem ser usados em uma forma Blazor (EditForm).
  • Usa a entrada booleana de uma caixa de seleção.
  • Define a cor de plano de fundo de seu contêiner <div> com base no estado da caixa de seleção, que ocorre quando o método AfterChange é executado após a vinculação (@bind:after).
  • É necessário substituir o método TryParseValueFromString da classe base, mas não processa dados de entrada de cadeia de caracteres porque uma caixa de seleção não fornece dados de cadeia de caracteres. Exemplos de implementações de TryParseValueFromString para outros tipos de componentes de entrada que processam a entrada de cadeia de caracteres estão disponíveis na fonte de referência ASP.NET Core.

Observação

Os links de documentação para a fonte de referência do .NET geralmente carregam a ramificação padrão do repositório, que representa o desenvolvimento atual para a próxima versão do .NET. Para selecionar uma tag para uma versão específica, use a lista suspensa "Alternar ramificações ou tags". Para obter mais informações, consulte Como selecionar uma marca de versão do código-fonte do ASP.NET Core (dotnet/AspNetCore.Docs #26205).

EngineeringApprovalInputDerived.razor:

@using System.Diagnostics.CodeAnalysis
@inherits InputBase<bool>

<div class="@divCssClass">
    <label>
        Engineering Approval:
        <input @bind="CurrentValue" @bind:after="AfterChange" class="@CssClass" 
            type="checkbox" />
    </label>
</div>

@code {
    private string? divCssClass;

    private void AfterChange()
    {
        divCssClass = CurrentValue ? "bg-success text-white" : null;
    }

    protected override bool TryParseValueFromString(
        string? value, out bool result, 
        [NotNullWhen(false)] out string? validationErrorMessage)
            => throw new NotSupportedException(
                "This component does not parse string inputs. " +
                $"Bind to the '{nameof(CurrentValue)}' property, " +
                $"not '{nameof(CurrentValueAsString)}'.");
}

Para usar o componente anterior no formulário de exemplo de nave estelar (Starship3.razor/Starship.cs), substitua o bloco <div> para o campo de aprovação de engenharia por uma instância de componente EngineeringApprovalInputDerived vinculada à propriedade IsValidatedDesign do modelo:

- <div>
-     <label>
-         Engineering Approval: 
-         <InputCheckbox @bind-Value="Model!.IsValidatedDesign" />
-     </label>
- </div>
+ <EngineeringApprovalInputDerived @bind-Value="Model!.IsValidatedDesign" />

Se o componente que herda de InputBase<TValue> for alguma vez renderizado estaticamente, atribua a propriedade InputBase<TValue>.NameAttributeValue ao atributo name dos elementos <input>.

<input @bind="CurrentValue" @bind:after="AfterChange" class="@CssClass"
    type="checkbox" name="@NameAttributeValue" />

A atribuição anterior não é necessária se o componente tiver a garantia de sempre renderizar interativamente.

Componente de entrada com controle total do desenvolvedor

O seguinte componente de exemplo:

  • Não herda de InputBase<TValue>. O componente assume o controle total do processamento de entrada, incluindo vinculação, retornos de chamada e validação. O componente pode ser usado dentro ou fora de uma forma Blazor (EditForm).
  • Usa a entrada booleana de uma caixa de seleção.
  • Altera a cor do plano de fundo se a caixa de seleção estiver marcada.

O código no componente inclui:

  • A propriedade Value é usada com ligação bidirecional para obter ou definir o valor da entrada. ValueChanged é o callback que atualiza o valor associado.

  • Quando utilizado num formato Blazor:

    • O EditContext é um valor em cascata .
    • fieldCssClass estiliza o campo com base no resultado da validação EditContext.
    • ValueExpression é uma expressão (Expression<Func<T>>) atribuída pela estrutura que identifica o valor vinculado.
    • FieldIdentifier identifica exclusivamente um único campo que pode ser editado, geralmente correspondendo a uma propriedade de modelo. O identificador de campo é criado com a expressão que identifica o valor vinculado (ValueExpression).
  • No manipulador de eventos OnChange:

    • O valor da entrada da caixa de seleção é obtido de InputFileChangeEventArgs.
    • A cor do plano de fundo e a cor do texto do elemento <div> contêiner são definidas.
    • EventCallback.InvokeAsync invoca o delegado associado à vinculação e envia uma notificação de evento aos consumidores indicando que o valor mudou.
    • Se o componente for usado em um EditForm (a propriedade EditContext não é null), EditContext.NotifyFieldChanged será chamado para acionar a validação.

EngineeringApprovalInputStandalone.razor:

@using System.Globalization
@using System.Linq.Expressions

<div class="@divCssClass">
    <label>
        Engineering Approval:
        <input class="@fieldCssClass" @onchange="OnChange" type="checkbox" 
            value="@Value" />
    </label>
</div>

@code {
    private string? divCssClass;
    private FieldIdentifier fieldIdentifier;
    private string? fieldCssClass => EditContext?.FieldCssClass(fieldIdentifier);

    [CascadingParameter]
    private EditContext? EditContext { get; set; }

    [Parameter]
    public bool? Value { get; set; }

    [Parameter]
    public EventCallback<bool> ValueChanged { get; set; }

    [Parameter]
    public Expression<Func<bool>>? ValueExpression { get; set; }

    protected override void OnInitialized()
    {
        fieldIdentifier = FieldIdentifier.Create(ValueExpression!);
    }

    private async Task OnChange(ChangeEventArgs args)
    {
        BindConverter.TryConvertToBool(args.Value, CultureInfo.CurrentCulture, 
            out var value);

        divCssClass = value ? "bg-success text-white" : null;

        await ValueChanged.InvokeAsync(value);
        EditContext?.NotifyFieldChanged(fieldIdentifier);
    }
}

Para usar o componente anterior no formulário de exemplo de nave estelar (Starship3.razor/Starship.cs), substitua o bloco <div> para o campo de aprovação de engenharia por uma instância de componente EngineeringApprovalInputStandalone vinculada à propriedade IsValidatedDesign do modelo:

- <div>
-     <label>
-         Engineering Approval: 
-         <InputCheckbox @bind-Value="Model!.IsValidatedDesign" />
-     </label>
- </div>
+ <EngineeringApprovalInputStandalone @bind-Value="Model!.IsValidatedDesign" />

O componente EngineeringApprovalInputStandalone também é funcional fora de um EditForm:

<EngineeringApprovalInputStandalone @bind-Value="ValidDesign" />

<div>
    <b>ValidDesign:</b> @ValidDesign
</div>

@code {
    private bool ValidDesign { get; set; }
}

Botões de opção

O exemplo nesta seção é baseado no formulário Starfleet Starship Database (componenteStarship3) da seção de exemplo do formulário deste artigo.

Adicione os seguintes tipos de enum ao aplicativo. Crie um novo arquivo para mantê-los ou adicione-os ao arquivo Starship.cs.

public class ComponentEnums
{
    public enum Manufacturer { SpaceX, NASA, ULA, VirginGalactic, Unknown }
    public enum Color { ImperialRed, SpacecruiserGreen, StarshipBlue, VoyagerOrange }
    public enum Engine { Ion, Plasma, Fusion, Warp }
}

Torne a classe ComponentEnums acessível a:

  • Modelo Starship em Starship.cs (por exemplo, using static ComponentEnums;).
  • Formulário Starfleet Starship Database (Starship3.razor) (por exemplo, @using static ComponentEnums).

Use InputRadio<TValue> componentes com o componente InputRadioGroup<TValue> para criar um grupo de botões de opção. No exemplo a seguir, as propriedades são adicionadas ao modelo de descrito na seção formulário Exemplo de do artigo componentes de entrada :

[Required]
[Range(typeof(Manufacturer), nameof(Manufacturer.SpaceX), 
    nameof(Manufacturer.VirginGalactic), ErrorMessage = "Pick a manufacturer.")]
public Manufacturer Manufacturer { get; set; } = Manufacturer.Unknown;

[Required, EnumDataType(typeof(Color))]
public Color? Color { get; set; } = null;

[Required, EnumDataType(typeof(Engine))]
public Engine? Engine { get; set; } = null;

Atualize o formulário (componente) da seção do formulário Exemplo do artigo componentes de entrada . Adicione os componentes para produzir:

  • Um grupo de botões de opção para o construtor naval.
  • Um grupo de botões de opção aninhado para a cor do motor e da embarcação.

Observação

Os grupos de botões de opção aninhados não são frequentemente usados em formulários porque podem resultar num layout desorganizado dos controlos do formulário que pode confundir os utilizadores. No entanto, há casos em que eles fazem sentido no design da interface do usuário, como no exemplo a seguir que emparelha recomendações para duas entradas do usuário, motor do navio e cor do navio. Um motor e uma cor são exigidos pela validação do formulário. O layout do formulário usa InputRadioGroup<TValue>s aninhados para emparelhar recomendações de motor e cor. No entanto, o usuário pode combinar qualquer mecanismo com qualquer cor para enviar o formulário.

Observação

Certifique-se de disponibilizar a classe ComponentEnums para o componente para o exemplo a seguir:

@using static ComponentEnums
<fieldset>
    <legend>Manufacturer</legend>
    <InputRadioGroup @bind-Value="Model!.Manufacturer">
        @foreach (var manufacturer in Enum.GetValues<Manufacturer>())
        {
            <div>
                <label>
                    <InputRadio Value="manufacturer" />
                    @manufacturer
                </label>
            </div>
        }
    </InputRadioGroup>
</fieldset>

<fieldset>
    <legend>Engine and Color</legend>
    <p>
        Engine and color pairs are recommended, but any
        combination of engine and color is allowed.
    </p>
    <InputRadioGroup Name="engine" @bind-Value="Model!.Engine">
        <InputRadioGroup Name="color" @bind-Value="Model!.Color">
            <div style="margin-bottom:5px">
                <div>
                    <label>
                        <InputRadio Name="engine" Value="Engine.Ion" />
                        Ion
                    </label>
                </div>
                <div>
                    <label>
                        <InputRadio Name="color" Value="Color.ImperialRed" />
                        Imperial Red
                    </label>
                </div>
            </div>
            <div style="margin-bottom:5px">
                <div>
                    <label>
                        <InputRadio Name="engine" Value="Engine.Plasma" />
                        Plasma
                    </label>
                </div>
                <div>
                    <label>
                        <InputRadio Name="color" Value="Color.SpacecruiserGreen" />
                        Spacecruiser Green
                    </label>
                </div>
            </div>
            <div style="margin-bottom:5px">
                <div>
                    <label>
                        <InputRadio Name="engine" Value="Engine.Fusion" />
                        Fusion
                    </label>
                </div>
                <div>
                    <label>
                        <InputRadio Name="color" Value="Color.StarshipBlue" />
                        Starship Blue
                    </label>
                </div>
            </div>
            <div style="margin-bottom:5px">
                <div>
                    <label>
                        <InputRadio Name="engine" Value="Engine.Warp" />
                        Warp
                    </label>
                </div>
                <div>
                    <label>
                        <InputRadio Name="color" Value="Color.VoyagerOrange" />
                        Voyager Orange
                    </label>
                </div>
            </div>
        </InputRadioGroup>
    </InputRadioGroup>
</fieldset>

Observação

Se Name for omitido, os componentes de InputRadio<TValue> são agrupados por seus ancestrais mais recentes.

Se tiver implementado a marcação de anterior no formulário de Exemplo de secção do componente , no artigo Componentes de entrada , atualize o registo para o método :

Logger.LogInformation("Id = {Id} Description = {Description} " +
    "Classification = {Classification} MaximumAccommodation = " +
    "{MaximumAccommodation} IsValidatedDesign = " +
    "{IsValidatedDesign} ProductionDate = {ProductionDate} " +
    "Manufacturer = {Manufacturer}, Engine = {Engine}, " +
    "Color = {Color}",
    Model?.Id, Model?.Description, Model?.Classification,
    Model?.MaximumAccommodation, Model?.IsValidatedDesign,
    Model?.ProductionDate, Model?.Manufacturer, Model?.Engine, 
    Model?.Color);

Ao trabalhar com botões de rádio num formulário, a ligação de dados é tratada de forma diferente de outros elementos porque os botões de rádio são avaliados como um grupo. O valor de cada botão é fixo, mas o valor do grupo de botões é determinado pelo botão selecionado. O exemplo a seguir mostra como:

  • Gerir a vinculação de dados para um grupo de botões de seleção.
  • Suporte à validação usando um componente InputRadio<TValue> personalizado.

InputRadio.razor:

@using System.Globalization
@inherits InputBase<TValue>
@typeparam TValue

<input @attributes="AdditionalAttributes" type="radio" value="@SelectedValue" 
       checked="@(SelectedValue.Equals(Value))" @onchange="OnChange" />

@code {
    [Parameter]
    public TValue SelectedValue { get; set; }

    private void OnChange(ChangeEventArgs args)
    {
        CurrentValueAsString = args.Value.ToString();
    }

    protected override bool TryParseValueFromString(string value, 
        out TValue result, out string errorMessage)
    {
        var success = BindConverter.TryConvertTo<TValue>(
            value, CultureInfo.CurrentCulture, out var parsedValue);
        if (success)
        {
            result = parsedValue;
            errorMessage = null;

            return true;
        }
        else
        {
            result = default;
            errorMessage = "The field isn't valid.";

            return false;
        }
    }
}

Para obter mais informações sobre parâmetros de tipo genéricos (@typeparam), consulte os seguintes artigos:

Use o modelo de exemplo a seguir.

StarshipModel.cs:

using System.ComponentModel.DataAnnotations;

namespace BlazorServer80
{
    public class Model
    {
        [Range(1, 5)]
        public int Rating { get; set; }
    }
}

O componente RadioButtonExample a seguir usa o componente InputRadio anterior para obter e validar uma classificação do usuário:

RadioButtonExample.razor:

@page "/radio-button-example"
@using System.ComponentModel.DataAnnotations
@using Microsoft.Extensions.Logging
@inject ILogger<RadioButtonExample> Logger

<h1>Radio Button Example</h1>

<EditForm Model="Model" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    @for (int i = 1; i <= 5; i++)
    {
        <div>
            <label>
                <InputRadio name="rate" SelectedValue="i" 
                    @bind-Value="Model.Rating" />
                @i
            </label>
        </div>
    }

    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

<div>@Model.Rating</div>

@code {
    public StarshipModel Model { get; set; }

    protected override void OnInitialized() => Model ??= new();

    private void HandleValidSubmit()
    {
        Logger.LogInformation("HandleValidSubmit called");
    }
}