Associação de formulários do ASP.NET Core 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.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a Política de Suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 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 a versão atual, consulte a versão .NET 9 deste artigo.
Este artigo explica como usar a associação em formulários Blazor.
EditForm
/EditContext
model
Um EditForm cria um EditContext com base na instância de modelo atribuída como um valor em cascata para outros componentes no formulário. O EditContext rastreia metadados sobre o processo de edição, incluindo quais campos foram modificados e as mensagens de validação atuais. A atribuição a um EditForm.Modelou a um EditForm.EditContext pode associar um formulário a dados.
Model binding
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 associa formulários a propriedades C#, mas também há suporte para associação de campo C#.
Associaçã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);
}
}
A atribuição de um EditContextou de um Model a um EditForm. Se ambos forem atribuídos, um erro de runtime será gerado.
Tipos com suporte
A associação dá suporte a:
- 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 associação de modelo. Use esses atributos para renomear propriedades, ignorar propriedades e marcar propriedades conforme necessário.
Opções de associação adicionais
Opções de associação de modelo adicionais estão disponíveis de RazorComponentsServiceOptions ao chamar AddRazorComponents:
- MaxFormMappingCollectionSize: número máximo de elementos permitidos em uma coleção de formulários.
- MaxFormMappingRecursionDepth: profundidade máxima permitida ao mapear recursivamente os dados do formulário.
- MaxFormMappingErrorCount: número máximo de erros permitidos ao mapear dados do formulário.
- MaxFormMappingKeySize: tamanho máximo do buffer usado para ler chaves de dados do formulário.
Veja a seguir 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ário
Use o parâmetro FormName para atribuir um nome de formulário. Os nomes de formulário devem ser exclusivos para associar dados de modelo. O formulário a seguir é chamado RomulanAle
:
<EditForm ... FormName="RomulanAle" ...>
...
</EditForm>
Fornecendo um nome de formulário:
- É necessário em todos os formulários enviados por componentes do servidor 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 a fim de evitar erros de postagem de formulário de runtime se a interatividade for removida em um formulário.
O nome do formulário só é verificado quando o formulário é postado em um ponto de extremidade como uma solicitação HTTP POST tradicional de um componente do servidor renderizado estaticamente. A estrutura não gera 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 (cadeia de caracteres vazia) acima do componente raiz do aplicativo, o que é suficiente quando não há colisões de nomes de formulário no aplicativo. Se houver possibilidade de colisões de nomes de formulário, como ao incluir um formulário a partir de uma biblioteca e você não tiver controle do nome do formulário usado pelo desenvolvedor 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 nomeado Hello
que 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 do formulário (Hello
), os nomes de formulário não colidem e os eventos são roteados para o formulário correto para eventos POST de 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;
}
Fornecer um parâmetro do formulário ([SupplyParameterFromForm]
)
O atributo [SupplyParameterFromForm]
indica que o valor da propriedade associada deve ser fornecido dos dados do formulário para o formulário. Os dados na solicitação que correspondem ao nome da propriedade estão associados à propriedade. Entradas baseadas em InputBase<TValue>
gera de nomes de valor de formulário que correspondem aos nomes que o Blazor usa para associação de modelo. Ao contrário das propriedades do parâmetro do componente ([Parameter]
), as propriedades anotadas com [SupplyParameterFromForm]
não precisam ser marcadas public
.
Você pode especificar os seguintes parâmetros de associação de formulário para o atributo[SupplyParameterFromForm]
:
- Name: obtém ou define o nome do 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 associado.
- FormName: obtém ou define o nome do manipulador. O nome é usado para corresponder 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 associa independentemente dois formulários a seus modelos por nome de 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 associar formulários
As diretrizes a seguir demonstram como aninhar e associar formulários filho.
A classe de detalhes da nave a seguir (ShipDetails
) contém uma descrição e o 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
a seguir nomeia um identificador (Id
) e inclui os detalhes da nave.
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> garante que o componente filho gere os nomes de campo de formulário corretos com base no modelo (T
), em que 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 é associado à classe Ship
. O componente StarshipSubform
é usado para editar detalhes da nave, associados 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, o método de ciclo de vida OnInitialized{Async}
e o método de ciclo de vida OnParametersSet{Async}
são acionados quando o componente é inicialmente renderizado e em cada formulário POST para o servidor. Para inicializar os valores do 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 avançados de erro de mapeamento de formulário
A estrutura cria uma instância e preenche o FormMappingContext para um formulário, que é o contexto associado à operação de mapeamento de um determinado formulário. Cada escopo de mapeamento (definido por um componenteFormMappingScope) cria uma instância 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 ele é principalmente uma fonte de dados para InputBase<TValue>, EditContext e 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 um código personalizado que consome os valores tentados e erros de mapeamento.
Componentes personalizados de entrada
Para cenários de processamento de entrada personalizada, as subseções a seguir demonstram componentes de entrada personalizados:
Componente de entrada baseado em
InputBase<T>
: o componente herda de InputBase<TValue>, que fornece uma implementação de base para associação, retornos de chamada e validação. Os componentes que herdam de InputBase<TValue> devem ser usados em um formulário Blazor (EditForm).Componente de entrada com controle total do desenvolvedor: o componente assume o controle total do processamento de entrada. O código do componente deve gerenciar a associação, os retornos de chamada e a validação. O componente pode ser usado dentro ou fora de um formulário Blazor.
Recomendamos que você derive seus componentes de entrada personalizados de InputBase<TValue>, a menos que requisitos específicos impeçam você de fazer isso. A classe InputBase<TValue> é mantida ativamente pela equipe do ASP.NET Core, garantindo que ela permaneça atualizada com os recursos e alterações de estrutura mais recentes do Blazor.
Componente de entrada com base em InputBase<T>
O componente de exemplo a seguir:
- Herdada de InputBase<TValue>. Os componentes que herdam de InputBase<TValue> devem ser usados em um formulário Blazor (EditForm).
- Obtém a entrada booliana de uma caixa de seleção.
- Define a cor da tela de fundo de seu contêiner
<div>
com base no estado da caixa de seleção, que ocorre quando o métodoAfterChange
é executado após a associação (@bind:after
). - É necessário para 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 deTryParseValueFromString
para outros tipos de componentes de entrada que processam a entrada de strings 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 o branch padrão do repositório, que representa o desenvolvimento atual da próxima versão do .NET. Para selecionar uma marca para uma versão específica, use a lista suspensa para Alternar branches ou marcas. Para saber mais, confira 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 starship (Starship3.razor
/Starship.cs
), substitua o bloco <div>
do 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" />
Quando o componente que herda de InputBase<TValue> for renderizado de forma estática, 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 será necessária se o componente tiver a garantia de sempre ser renderizado interativamente.
Componente de entrada com controle total do desenvolvedor
O componente de exemplo a seguir:
- Não herda de InputBase<TValue>. O componente assume o controle total do processamento de entrada, incluindo associação, retornos de chamada e validação. O componente pode ser usado dentro ou fora de um formulário Blazor (EditForm).
- Obtém a entrada booliana de uma caixa de seleção.
- Altera a cor da tela de fundo se a caixa de seleção estiver marcada.
O código no componente inclui:
A propriedade
Value
é usada com vinculação bidirecional para obter ou definir o valor da entrada.ValueChanged
é o retorno de chamada que atualiza o valor associado.Quando usado em um formulário 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 associado.- 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 associado (
ValueExpression
).
No manipulador de eventos
OnChange
:- O valor da entrada da caixa de seleção é obtido de InputFileChangeEventArgs.
- A cor da tela de fundo e a cor do texto do elemento de contêiner
<div>
são definidas. - EventCallback.InvokeAsync invoca o delegado associado à associação e envia uma notificação de evento aos consumidores de que o valor foi alterado.
- Se o componente for usado em um EditForm (a propriedade
EditContext
não énull
), EditContext.NotifyFieldChanged é chamado para disparar 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 starship (Starship3.razor
/Starship.cs
), substitua o bloco <div>
do 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 baseia-se no formulário Starfleet Starship Database
(componente Starship3
) da seção Formulário de exemplo deste artigo.
Adicione os seguintes enum
tipos ao aplicativo. Crie um 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 ao:
- Modelo
Starship
emStarship.cs
(por exemplo,using static ComponentEnums;
). - Formulário
Starfleet Starship Database
(Starship3.razor
) (por exemplo,@using static ComponentEnums
).
Use componentes InputRadio<TValue> 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 Starship
descrito na seção Formulário de exemplo 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 Starfleet Starship Database
(componente Starship3
) da seção Formulário de exemplo do artigo Componentes de entrada. Adicione os componentes para produzir:
- Um grupo de botões de opção para o fabricante do navio.
- Um grupo de botões de opção aninhado para a cor do motor e do navio.
Observação
Os grupos de botões de opção aninhados não são frequentemente usados em formulários porque podem resultar em um layout de controles de formulário desorganizado que pode confundir os usuários. No entanto, há casos em que eles fazem sentido no design da interface do usuário, como no exemplo a seguir, que junta recomendações para duas entradas de usuário, o motor 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 juntar recomendações de motor e cor. No entanto, o usuário pode combinar qualquer motor com qualquer cor para enviar o formulário.
Observação
Certifique-se de disponibilizar a classe ComponentEnums
para o componente no seguinte exemplo:
@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 InputRadio<TValue> serão agrupados por seu ancestral mais recente.
Se você implementou a marcação Razor anterior no componente Starship3
da seção Formulário de exemplo do artigo Componentes de entrada, atualize o registro em log do método Submit
:
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 opção em um formulário, a associação de dados será tratada de forma diferente de outros elementos porque os botões de opção são avaliados como um grupo. O valor de cada botão de opção é fixo, mas o valor do grupo de botões de opção é o valor do botão de opção selecionado. O exemplo a seguir mostra como:
- Manipule a associação de dados para um grupo de botões de opção.
- Dê 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érico (@typeparam
), confira os seguintes artigos:
- Referência de sintaxe do Razor para ASP.NET Core
- Componentes Razor do ASP.NET Core
- Componentes com modelo Blazor no ASP.NET Core
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 seguinte componente RadioButtonExample
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");
}
}