enlace de formularios de Blazor ASP.NET Core
Nota:
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Advertencia
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta la versión .NET 8 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión de .NET 9 de este artículo.
En este artículo se explica cómo usar el enlace en formularios Blazor.
EditForm
/EditContext
modelo
Un componente EditForm crea un objeto EditContext basado en el objeto asignado como un valor en cascada para otros componentes del formulario. El componente EditContext hace seguimiento de los metadatos del proceso de edición, los incluidos los campos del formulario que se han modificado y los mensajes de validación actuales. La asignación a EditForm.Model o a EditForm.EditContext puede enlazar un formulario a los datos.
Enlace de modelos
Asignación 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();
}
Nota
La mayoría de los ejemplos del modelo de formulario de este artículo enlazan formularios a propiedades de C#, pero también se admite el enlace de campos de C#.
Enlace de contexto
Asignación 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);
}
}
Asigne un EditContext o Model a un EditForm. Si se asignan ambos, se produce un error en el momento de ejecutar.
Tipos admitidos
El enlace admite:
- Tipos primitivos
- Colecciones
- Tipos complejos
- Tipos recursivos
- Tipos con constructores
- Enumeraciones
También puede usar los atributos [DataMember]
y [IgnoreDataMember]
para personalizar el enlace de modelos. Use estos atributos para cambiar el nombre de las propiedades, omitir las propiedades y marcar las propiedades según sea necesario.
Opciones de enlace adicionales
Hay disponibles opciones de enlace de modelos adicionales desde RazorComponentsServiceOptions al llamar a AddRazorComponents:
- MaxFormMappingCollectionSize: número máximo de elementos permitidos en una colección de formularios.
- MaxFormMappingRecursionDepth: profundidad máxima permitida cuando se asignan de forma recursiva los datos del formulario.
- MaxFormMappingErrorCount: número máximo de errores permitidos al asignar datos de formulario.
- MaxFormMappingKeySize: tamaño máximo del búfer usado para leer las claves de datos del formulario.
A continuación se muestran los valores predeterminados asignados por el marco:
builder.Services.AddRazorComponents(options =>
{
options.FormMappingUseCurrentCulture = true;
options.MaxFormMappingCollectionSize = 1024;
options.MaxFormMappingErrorCount = 200;
options.MaxFormMappingKeySize = 1024 * 2;
options.MaxFormMappingRecursionDepth = 64;
}).AddInteractiveServerComponents();
Nombres de formulario
Use el parámetro FormName para asignar un nombre de formulario. Los nombres de los formularios deben ser únicos para poder enlazar los datos del modelo. El siguiente formulario se denomina RomulanAle
:
<EditForm ... FormName="RomulanAle" ...>
...
</EditForm>
Proporcionar un nombre de formulario:
- Es necesario para todos los formularios enviados por componentes del lado servidor representados estáticamente.
- No es necesario para los formularios enviados por componentes representados de forma interactiva, que incluye formularios en aplicaciones y componentes de Blazor WebAssembly con un modo de representación interactivo. Sin embargo, se recomienda proporcionar un nombre de formulario único para cada formulario para evitar errores de contabilización de formularios en tiempo de ejecución si alguna vez se quita la interactividad de un formulario.
El nombre del formulario solo se comprueba cuando el formulario se publica en un punto de conexión como una solicitud HTTP POST tradicional desde un componente del lado servidor representado estáticamente. El marco no produce una excepción en el punto de representación de un formulario, sino solo en el momento en el que llega un HTTP POST y no especifica un nombre de formulario.
Hay un ámbito de formulario sin nombre (cadena vacía) encima del componente raíz de la aplicación, lo que basta cuando no hay colisiones de nombres de formulario en la aplicación. Si es posible que existan colisiones de nombres de formularios, como cuando se incluye un formulario de una biblioteca y no se tiene control sobre el nombre del formulario utilizado por el desarrollador de la biblioteca, proporciona un ámbito de nombre de formulario con el componente FormMappingScope en el proyecto principal del objeto Blazor Web App.
En el ejemplo siguiente, el componente HelloFormFromLibrary
tiene un formulario denominado Hello
y está en una 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;
}
El siguiente componente de NamedFormsWithScope
usa el componente HelloFormFromLibrary
de la biblioteca y también tiene un formulario denominado Hello
. El FormMappingScope nombre del ámbito del componente es ParentContext
para los formularios proporcionados por el HelloFormFromLibrary
componente. Aunque ambos formularios de este ejemplo tienen el nombre del formulario (Hello
), los nombres de formulario no colisionan y los eventos se enrutan al formulario correcto para los eventos POST del formulario.
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;
}
Proporciona un parámetro del formulario ([SupplyParameterFromForm]
)
El atributo [SupplyParameterFromForm]
indica que el valor de la propiedad asociada debe proporcionarse a partir de los datos del formulario para el formulario. Los datos de la solicitud que coinciden con el nombre de la propiedad están enlazados a la propiedad . Las entradas basadas en InputBase<TValue>
generan nombres de valor de formulario que coinciden con los nombres que Blazor usa para el enlace de modelos. A diferencia de las propiedades del parámetro de componente ([Parameter]
), no es necesario marcar las propiedades anotadas public
con [SupplyParameterFromForm]
.
Puedes especificar los siguientes parámetros de enlace de formulario para el atributo [SupplyParameterFromForm]
:
- Name: se obtiene o establece para el nombre del parámetro. El nombre se usa para determinar el prefijo que se va a usar para buscar coincidencias con los datos del formulario y decidir si es necesario enlazar o no el valor.
- FormName: se obtiene o establece para el nombre del controlador. El nombre se usa para hacer coincidir el parámetro con el formulario por el nombre del formulario y poder así decidir si es necesario enlazar o no el valor.
En el ejemplo siguiente se enlazan de forma independiente dos formularios a sus modelos por nombre de formulario.
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; }
}
}
Anidar y enlazar formularios
En las instrucciones siguientes se muestra cómo anidar y enlazar formularios secundarios.
La siguiente clase de detalles de envío (ShipDetails
) contiene una descripción y una longitud para un subformulario.
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; }
}
Los siguientes nombres de clase de Ship
asigna un nombre a un identificador (Id
) e incluye los detalles del envío.
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();
}
}
El siguiente subformulario se usa para editar valores del tipo ShipDetails
. Esto se implementa heredando Editor<T> en la parte superior del componente. Editor<T> garantiza que el componente secundario genere los nombres de campo de formulario correctos basados en el modelo (T
), donde T
en el ejemplo siguiente es 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>
El formulario principal está enlazado a la clase Ship
. El componente StarshipSubform
se usa para editar los detalles del envío, enlazados 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);
}
Inicialización de datos de formulario con SSR estática
Cuando un componente adopta una SSR estática, el método de ciclo de vida OnInitialized{Async}
y el método de ciclo de vida OnParametersSet{Async}
se activan cuando el componente se representa inicialmente y en cada formulario POST en el servidor. Para inicializar los valores del modelo de formulario, confirma si el modelo ya tiene datos antes de asignar nuevos valores de modelo en OnParametersSet{Async}
, como se muestra en el ejemplo siguiente.
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; }
}
}
Escenarios avanzados de error de asignación de formularios
El marco crea una instancia de y rellena el FormMappingContext de un formulario, que es el contexto asociado a la operación de asignación de un formulario determinado. Cada ámbito de asignación (definido por un componente de FormMappingScope) crea instancias de FormMappingContext. Cada vez que un [SupplyParameterFromForm]
solicita el contexto de un valor, el marco rellena el FormMappingContext con el valor intentado y los errores de asignación.
No se espera que los desarrolladores interactúen directamente con FormMappingContext, ya que es principalmente un origen de datos para InputBase<TValue>, EditContext y otras implementaciones internas para mostrar errores de asignación como errores de validación. En escenarios personalizados avanzados, los desarrolladores pueden acceder a FormMappingContext directamente como un [CascadingParameter]
para escribir código personalizado que consume los valores intentados y los errores de asignación.
Componentes de entrada personalizados
Para escenarios de procesamiento de entrada personalizados, las subsecciones siguientes muestran componentes de entrada personalizados:
Componente de entrada basado en
InputBase<T>
: el componente hereda de InputBase<TValue>, que proporciona una base de implementación para enlace, devoluciones de llamada y validación. Los componentes que heredan de InputBase<TValue> deben usarse en un formulario Blazor (EditForm).Componente de entrada con control de desarrollador total: el componente toma el control total del procesamiento de entrada. El código del componente debe administrar enlace, devoluciones de llamada y validación. El componente se puede usar dentro o fuera de un formulario Blazor.
Se recomienda derivar los componentes de entrada personalizados InputBase<TValue> a menos que los requisitos específicos le impidan hacerlo. El equipo de ASP.NET Core mantiene activamente la clase InputBase<TValue>, lo que garantiza que se mantenga actualizada con las características Blazor y los cambios más recientes del marco.
Componente de entrada basado en InputBase<T>
En el ejemplo de componente siguiente:
- Se hereda de InputBase<TValue>. Los componentes que heredan de InputBase<TValue> deben usarse en un formulario Blazor (EditForm).
- Toma la entrada booleana de una casilla.
- Establece el color de fondo de su contenedor
<div>
en función del estado de la casilla, que se produce cuando se ejecuta el métodoAfterChange
después del enlace (@bind:after
). - Es necesario para invalidar el método
TryParseValueFromString
de la clase base, pero no procesa los datos de entrada de cadena porque una casilla no proporciona datos de cadena. Las implementaciones de ejemplo deTryParseValueFromString
para otros tipos de componentes de entrada que procesan la entrada de cadena están disponibles en el origen de referencia de ASP.NET Core.
Nota:
Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta de una versión específica, usa la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, consulta Procedimientos para seleccionar una etiqueta de versión de código fuente de 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 el componente anterior en el formulario de ejemplo de nave (Starship3.razor
/Starship.cs
) reemplaza el bloque <div>
del campo de aprobación de ingeniería por una instancia de componente EngineeringApprovalInputDerived
enlazada a la propiedad IsValidatedDesign
del modelo:
- <div>
- <label>
- Engineering Approval:
- <InputCheckbox @bind-Value="Model!.IsValidatedDesign" />
- </label>
- </div>
+ <EngineeringApprovalInputDerived @bind-Value="Model!.IsValidatedDesign" />
Componente de entrada con control de desarrollador total
En el ejemplo de componente siguiente:
- No hereda de InputBase<TValue>. El componente toma el control total del procesamiento de entrada, incluidos enlace, devoluciones de llamada y validación. El componente se puede usar dentro o fuera de un formulario Blazor (EditForm).
- Toma la entrada booleana de una casilla.
- Cambia el color de fondo si la casilla está activada.
El código del componente incluye:
La propiedad
Value
se usa con enlace bidireccional para obtener o establecer el valor de la entrada.ValueChanged
es la devolución de llamada que actualiza el valor enlazado.Cuando se usa en un formulario Blazor:
- EditContext es un valor en cascada.
fieldCssClass
diseña el campo en función del resultado de la validación EditContext.ValueExpression
es una expresión (Expression<Func<T>>
) asignada por el marco que identifica el valor enlazado.- FieldIdentifier identifica de forma única un solo campo que se puede editar, normalmente correspondiente a una propiedad de modelo. El identificador de campo se crea con la expresión que identifica el valor enlazado (
ValueExpression
).
En el controlador de eventos
OnChange
:- El valor de la entrada de casilla se obtiene de InputFileChangeEventArgs.
- Se establecen el color de fondo y el color de texto del elemento
<div>
de contenedor. - EventCallback.InvokeAsync invoca al delegado asociado al enlace y envía una notificación de evento a los consumidores de que el valor ha cambiado.
- Si el componente se usa en EditForm (la propiedad
EditContext
no esnull
), se lla a a EditContext.NotifyFieldChanged para desencadenar la validación.
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 el componente anterior en el formulario de ejemplo de nave (Starship3.razor
/Starship.cs
), reemplaza el bloque <div>
del campo de aprobación de ingeniería por una instancia de componente EngineeringApprovalInputStandalone
enlazada a la propiedad IsValidatedDesign
del modelo:
- <div>
- <label>
- Engineering Approval:
- <InputCheckbox @bind-Value="Model!.IsValidatedDesign" />
- </label>
- </div>
+ <EngineeringApprovalInputStandalone @bind-Value="Model!.IsValidatedDesign" />
El componente EngineeringApprovalInputStandalone
también funciona fuera de un objeto EditForm:
<EngineeringApprovalInputStandalone @bind-Value="ValidDesign" />
<div>
<b>ValidDesign:</b> @ValidDesign
</div>
@code {
private bool ValidDesign { get; set; }
}
Botones de radio
El ejemplo de esta sección se basa en el formulario Starfleet Starship Database
(componente Starship3
) de la sección Formulario de ejemplo de este artículo.
Agrega los siguientes tipos enum
a la aplicación. Crea un nuevo archivo para que los contenga o agrégalos al archivo 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 }
}
Haz que la clase ComponentEnums
sea accesible al:
- Modelo
Starship
enStarship.cs
(por ejemplo,using static ComponentEnums;
). - Formulario
Starfleet Starship Database
(Starship3.razor
) (por ejemplo,@using static ComponentEnums
).
Usa componentes InputRadio<TValue> con el componente InputRadioGroup<TValue> para crear un grupo de botones de radio. En el ejemplo siguiente, las propiedades se agregan al modelo de Starship
descrito en la sección Formulario ejemplo del artículo 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;
Actualiza el formulario de Starfleet Starship Database
(componenteStarship3
) del formulario de ejemplo de sección del artículo Componentes de entrada. Agrega los componentes para generar lo siguiente:
- Un grupo de botones de radio para el fabricante del envío.
- Un grupo de botones de radio anidados para el motor y el color de envío.
Nota
Los grupos de botones de radio anidados no se suelen usar en formularios porque pueden dar lugar a un diseño desorganizado de controles de formulario que pueden confundir a los usuarios. Sin embargo, hay casos en los que tienen sentido en el diseño de la interfaz de usuario, como en el ejemplo siguiente que combina recomendaciones para dos entradas de usuario, el motor de envío y el color del envío. La validación del formulario requiere un motor y un color. El diseño del formulario usa objetos anidados InputRadioGroup<TValue> para emparejar las recomendaciones de motor y color. Sin embargo, el usuario puede combinar cualquier motor con cualquier color para enviar el formulario.
Nota
Asegúrate de que la clase ComponentEnums
esté disponible para el componente para el ejemplo siguiente:
@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>
Nota
Si se omite Name
, los componentes de InputRadio<TValue> los agrupa su antecesor más reciente.
Si implementaste el marcado Razor anterior en el componente Starship3
de la sección Formulario de ejemplo del artículo Componentes de entrada, actualiza el registro del 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);
Al trabajar con botones de radio en un formulario, el enlace de datos se controla de manera diferente que otros elementos, ya que los botones de radio se evalúan como un grupo. El valor de cada botón de radio es fijo, pero el valor del grupo de botones de radio es el valor del botón de radio seleccionado. El ejemplo siguiente muestra cómo:
- Controlar el enlace de datos en un grupo de botones de radio
- Admitir la validación usando un 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 obtener más información sobre los parámetros de tipo genérico (@typeparam
), consulta los siguientes artículos:
- Referencia sobre la sintaxis de Razor para ASP.NET Core
- Componentes Razor de ASP.NET Core
- Componentes con plantilla de Blazor en ASP.NET Core
Usa el siguiente modelo de ejemplo.
StarshipModel.cs
:
using System.ComponentModel.DataAnnotations;
namespace BlazorServer80
{
public class Model
{
[Range(1, 5)]
public int Rating { get; set; }
}
}
El componente RadioButtonExample
siguiente usa el componente InputRadio
anterior para obtener y validar una clasificación del usuario:
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");
}
}