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 .NET 8 de este artículo.
Advertencia
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulte la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulte 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 .NET 8 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]
public 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]
public 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.
De forma predeterminada, 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, proporcione un alcance de nombre de formulario con el componente FormMappingScope en el proyecto principal de la aplicación web Blazor.
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]
public 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]
public string? Name { get; set; }
private void Submit() => submitted = true;
}
Proporcione 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.
Puede 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")]
public Holodeck? Model1 { get; set; }
[SupplyParameterFromForm(FormName = "Holodeck2")]
public 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; }
}
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();
}
}
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>
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]
public 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);
}
}
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.
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.
Agregue los siguientes tipos enum
a la aplicación. Cree un nuevo archivo para que los contenga o agréguelos 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 }
}
Haga 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
).
Use 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;
Actualice el formulario de Starfleet Starship Database
(componenteStarship3
) del formulario de ejemplo de sección del artículo Componentes de entrada. Agregue 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úrese 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 implementó el marcado Razor anterior en el componente Starship3
del formulario de ejemplo de sección del artículo Componentes de entrada, actualice 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
), consulte 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
Use 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");
}
}