Condividi tramite


ASP.NET binding dei moduli principali Blazor

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Avviso

Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Questo articolo illustra come usare l'associazione nei Blazor moduli.

EditForm / EditContext modello

Un EditForm oggetto crea un EditContext oggetto basato sull'oggetto assegnato come valore a catena per altri componenti nel form. Tiene EditContext traccia dei metadati relativi al processo di modifica, inclusi i campi modulo modificati e i messaggi di convalida correnti. L'assegnazione a un oggetto EditForm.Model o può EditForm.EditContext associare un modulo ai dati.

Associazione di modelli

Assegnazione 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 maggior parte degli esempi di modelli di modulo di questo articolo associa moduli alle proprietà C#, ma è supportata anche l'associazione di campi C#.

Associazione di contesto

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

Assegnare EditForm Se vengono assegnati entrambi, viene generato un errore di runtime.

Tipi supportati

L'associazione supporta:

  • Tipi primitivi
  • Raccolte
  • Tipi complessi
  • Tipi ricorsivi
  • Tipi con costruttori
  • Enumerazioni

È anche possibile usare gli attributi e [DataMember] per personalizzare l'associazione [IgnoreDataMember] di modelli. Usare questi attributi per rinominare le proprietà, ignorare le proprietà e contrassegnare le proprietà come richiesto.

Opzioni di associazione aggiuntive

Sono disponibili opzioni aggiuntive per l'associazione di modelli da RazorComponentsServiceOptions quando si chiama AddRazorComponents:

Di seguito vengono illustrati i valori predefiniti assegnati dal framework:

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

Nomi dei moduli

Usare il FormName parametro per assegnare un nome di modulo. I nomi dei moduli devono essere univoci per associare i dati del modello. Il formato seguente è denominato RomulanAle:

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

Specificare un nome di modulo:

  • È necessario per tutti i moduli inviati da componenti lato server sottoposti a rendering statico.
  • Non è necessario per i moduli inviati da componenti di cui è stato eseguito il rendering interattivo, che include moduli nelle app e nei Blazor WebAssembly componenti con una modalità di rendering interattiva. È tuttavia consigliabile specificare un nome di modulo univoco per ogni modulo per evitare errori di pubblicazione dei moduli in fase di esecuzione se l'interattività viene mai eliminata per un modulo.

Il nome del modulo viene controllato solo quando il modulo viene inviato a un endpoint come richiesta HTTP POST tradizionale da un componente lato server sottoposto a rendering statico. Il framework non genera un'eccezione al momento del rendering di un modulo, ma solo nel momento in cui arriva un HTTP POST e non specifica un nome di modulo.

Esiste un ambito di modulo senza nome (stringa vuota) sopra il componente radice dell'app, che è sufficiente quando non sono presenti conflitti di nomi di modulo nell'app. Se sono possibili conflitti di nome modulo, ad esempio quando si include un modulo da una libreria e non si dispone di alcun controllo del nome del modulo utilizzato dallo sviluppatore della libreria, fornire un ambito di nome modulo con il FormMappingScope componente nel Blazor Web Appprogetto principale del .

Nell'esempio seguente il HelloFormFromLibrary componente ha un form denominato Hello e si trova in una libreria.

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

Il componente seguente NamedFormsWithScope usa il componente della HelloFormFromLibrary libreria e ha anche un modulo denominato Hello. Il FormMappingScope nome dell'ambito del componente è ParentContext per tutti i moduli forniti dal HelloFormFromLibrary componente. Anche se entrambi i moduli in questo esempio hanno il nome del modulo (Hello), i nomi dei moduli non si scontrano e gli eventi vengono indirizzati al modulo corretto per gli eventi POST del modulo.

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

Specificare un parametro dal modulo ([SupplyParameterFromForm])

L'attributo [SupplyParameterFromForm] indica che il valore della proprietà associata deve essere fornito dai dati del modulo per il modulo. I dati nella richiesta che corrispondono al nome della proprietà sono associati alla proprietà . Input basati su InputBase<TValue> genera nomi di valori di modulo che corrispondono ai nomi Blazor usati per l'associazione di modelli. A differenza delle proprietà dei parametri del componente ([Parameter]), le proprietà annotate con [SupplyParameterFromForm] non devono essere contrassegnate come public.

È possibile specificare i parametri di associazione del modulo seguenti all'attributo [SupplyParameterFromForm]:

  • Name: ottiene o imposta il nome del parametro. Il nome viene utilizzato per determinare il prefisso da utilizzare per trovare le corrispondenze con i dati del modulo e decidere se il valore deve essere associato o meno.
  • FormName: ottiene o imposta il nome del gestore. Il nome viene utilizzato per associare il parametro al formato in base al nome del modulo per decidere se il valore deve essere associato o meno.

Nell'esempio seguente vengono associati in modo indipendente due forme ai relativi modelli in base al nome del modulo.

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

Annidare e associare moduli

Le indicazioni seguenti illustrano come annidare e associare moduli figlio.

La classe dettagli spedizione seguente (ShipDetails) contiene una descrizione e una lunghezza per una sottomaschera.

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

La classe seguente Ship assegna un nome a un identificatore (Id) e include i dettagli della spedizione.

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

La sottomaschera seguente viene utilizzata per la modifica dei valori del ShipDetails tipo. Questa operazione viene implementata ereditando Editor<T> nella parte superiore del componente. Editor<T> garantisce che il componente figlio generi i nomi dei campi modulo corretti in base al modello (T), dove T nell'esempio seguente è 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>

Il form principale è associato alla Ship classe . Il StarshipSubform componente viene usato per modificare i dettagli della spedizione, associati come 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);
}

Inizializzare i dati del modulo con SSR statico

Quando un componente adotta SSR statico, il OnInitialized{Async} metodo del ciclo di vita e il OnParametersSet{Async} del ciclo di vita vengono attivati quando viene inizialmente eseguito il rendering del componente e in ogni modulo POST nel server. Per inizializzare i valori del modello di modulo, verificare se il modello dispone già di dati prima di assegnare nuovi valori del modello in OnParametersSet{Async}, come illustrato nell'esempio seguente.

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

Scenari avanzati di errore di mapping dei moduli

Il framework crea un'istanza e popola l'oggetto FormMappingContext per una maschera, ovvero il contesto associato all'operazione di mapping di un modulo specificato. Ogni ambito di mapping (definito da un componente) crea FormMappingScopeun'istanza FormMappingContext di . Ogni volta che un chiede [SupplyParameterFromForm] al contesto un valore, il framework popola FormMappingContext con il valore tentato ed eventuali errori di mapping.

Gli sviluppatori non devono interagire direttamente con FormMappingContext , perché è principalmente un'origine di dati per InputBase<TValue>, EditContexte altre implementazioni interne per mostrare gli errori di mapping come errori di convalida. Negli scenari personalizzati avanzati gli sviluppatori possono accedere FormMappingContext direttamente come oggetto [CascadingParameter] per scrivere codice personalizzato che utilizza i valori tentati e gli errori di mapping.

Componenti di input personalizzati

Per scenari di elaborazione di input personalizzati, le sottosezioni seguenti illustrano i componenti di input personalizzati:

È consigliabile derivare i componenti di input personalizzati da InputBase<TValue> , a meno che non vengano evitati requisiti specifici. La InputBase<TValue> classe viene gestita attivamente dal team di ASP.NET Core, assicurandosi che rimanga aggiornata con le funzionalità e le modifiche del framework più recenti Blazor .

Componente di input basato su InputBase<T>

Componente di esempio seguente:

  • Eredita dall'oggetto InputBase<TValue>. I componenti che ereditano da InputBase<TValue> devono essere utilizzati in un Blazor modulo (EditForm).
  • Accetta l'input booleano da una casella di controllo.
  • Imposta il colore di sfondo del contenitore <div> in base allo stato della casella di controllo, che si verifica quando il metodo viene eseguito dopo l'associazione AfterChange (@bind:after).
  • È necessario per eseguire l'override del metodo della classe di TryParseValueFromString base, ma non elabora i dati di input della stringa perché una casella di controllo non fornisce dati stringa. Le implementazioni di esempio di TryParseValueFromString per altri tipi di componenti di input che elaborano l'input della stringa sono disponibili nell'origine di riferimento ASP.NET Core.

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - 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)}'.");
}

Per usare il componente precedente nel modulo di esempio starship (Starship3.razor/Starship.cs), sostituire il <div> blocco per il campo di approvazione della progettazione con un'istanza EngineeringApprovalInputDerived del componente associata alla proprietà del IsValidatedDesign modello:

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

Se viene eseguito il rendering statico del componente che eredita da InputBase<TValue>, assegnare la proprietà InputBase<TValue>.NameAttributeValue all'attributo name degli elementi <input>:

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

L'assegnazione precedente non è necessaria se il componente viene sempre eseguito in modo interattivo.

Componente di input con controllo completo per sviluppatori

Componente di esempio seguente:

  • Non eredita da InputBase<TValue>. Il componente acquisisce il controllo completo dell'elaborazione dell'input, tra cui associazione, callback e convalida. Il componente può essere utilizzato all'interno o all'esterno di un Blazor form (EditForm).
  • Accetta l'input booleano da una casella di controllo.
  • Modifica il colore di sfondo se la casella di controllo è selezionata.

Il codice nel componente include:

  • La Value proprietà viene utilizzata con l'associazione bidirezionale per ottenere o impostare il valore dell'input. ValueChanged è il callback che aggiorna il valore associato.

  • Se usato in un Blazor modulo:

    • EditContext è un valore a catena.
    • fieldCssClass stili il campo in base al risultato della EditContext convalida.
    • ValueExpression è un'espressione (Expression<Func<T>>) assegnata dal framework che identifica il valore associato.
    • FieldIdentifier identifica in modo univoco un singolo campo che può essere modificato, in genere corrispondente a una proprietà del modello. L'identificatore del campo viene creato con l'espressione che identifica il valore associato (ValueExpression).
  • OnChange Nel gestore eventi:

    • Il valore dell'input della casella di controllo viene ottenuto da InputFileChangeEventArgs.
    • Vengono impostati il colore di sfondo e il colore del testo dell'elemento contenitore <div> .
    • EventCallback.InvokeAsync richiama il delegato associato all'associazione e invia una notifica di evento ai consumer che il valore è stato modificato.
    • Se il componente viene usato in un oggetto EditForm (la proprietà non EditContextè ), null viene chiamato per attivare la EditContext.NotifyFieldChanged convalida.

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

Per usare il componente precedente nel modulo di esempio starship (Starship3.razor/Starship.cs), sostituire il <div> blocco per il campo di approvazione della progettazione con un'istanza EngineeringApprovalInputStandalone del componente associata alla proprietà del IsValidatedDesign modello:

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

Il EngineeringApprovalInputStandalone componente è anche funzionale all'esterno di un oggetto EditForm:

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

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

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

Pulsanti di opzione

L'esempio in questa sezione si basa sul Starfleet Starship Database modulo (Starship3 componente) della sezione Modulo di esempio di questo articolo.

Aggiungere i tipienum all'app. Creare un nuovo file per memorizzarli o aggiungerli al Starship.cs file.

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

Rendere la ComponentEnums classe accessibile a:

  • Starship modello in Starship.cs (ad esempio, using static ComponentEnums;).
  • Starfleet Starship Database form () (Starship3.razorad esempio, @using static ComponentEnums).

Usare InputRadio<TValue> i componenti con il InputRadioGroup<TValue> componente per creare un gruppo di pulsanti di opzione. Nell'esempio seguente le proprietà vengono aggiunte al Starship modello descritto nella sezione Modulo di esempio dell'articolo Componenti di input:

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

Aggiornare il Starfleet Starship Database modulo (Starship3 componente) della sezione Modulo di esempio dell'articolo Componenti di input. Aggiungere i componenti da produrre:

  • Gruppo di pulsanti di opzione per il produttore della nave.
  • Gruppo di pulsanti di opzione annidati per il motore e il colore della nave.

Nota

I gruppi di pulsanti di opzione annidati non vengono spesso usati nei moduli perché possono comportare un layout disorganizzato dei controlli modulo che possono confondere gli utenti. Tuttavia, esistono casi in cui hanno senso nella progettazione dell'interfaccia utente, ad esempio nell'esempio seguente che associa raccomandazioni per due input utente, motore di spedizione e colore spedizione. Un motore e un colore sono richiesti dalla convalida del modulo. Il layout del modulo usa s annidati InputRadioGroup<TValue>per associare le raccomandazioni relative al motore e ai colori. Tuttavia, l'utente può combinare qualsiasi motore con qualsiasi colore per inviare il modulo.

Nota

Assicurarsi di rendere disponibile la ComponentEnums classe per il componente per l'esempio seguente:

@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

Se Name viene omesso, InputRadio<TValue> i componenti vengono raggruppati in base al predecessore più recente.

Se il markup precedente Razor è stato implementato nel Starship3 componente della sezione Modulo di esempio dell'articolo Componenti di input, aggiornare la registrazione per il Submit metodo :

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

Quando si usano pulsanti di opzione in un modulo, il data binding viene gestito in modo diverso rispetto ad altri elementi perché i pulsanti di opzione vengono valutati come gruppo. Il valore di ogni pulsante di opzione è fisso, ma il valore del gruppo di pulsanti di opzione è il valore del pulsante di opzione selezionato. L'esempio seguente illustra come:

  • Gestire il data binding per un gruppo di pulsanti di opzione.
  • Supportare la convalida usando un componente personalizzato InputRadio<TValue> .

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

Per altre informazioni sui parametri di tipo generico (@typeparam), vedere gli articoli seguenti:

Usare il modello di esempio seguente.

StarshipModel.cs:

using System.ComponentModel.DataAnnotations;

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

Il componente seguente RadioButtonExample usa il componente precedente InputRadio per ottenere e convalidare una classificazione dall'utente:

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