Partager via


Liaison des formulaires Blazor ASP.NET Core

Remarque

Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 8 de cet article.

Avertissement

Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la Stratégie de prise en charge de .NET et .NET Core. Pour la version actuelle, consultez la version .NET 8 de cet article.

Important

Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.

Pour la version actuelle, consultez la version .NET 8 de cet article.

Cet article explique comment utiliser des liaisons dans des formulaires Blazor.

EditForm/EditContext modèle

Un EditForm crée un EditContext en fonction de l’objet affecté comme valeur en cascade pour d’autres composants du formulaire. Le EditContext suit les métadonnées relatives au processus de modification, y compris les champs de formulaire qui ont été modifiés et les messages de validation actuels. L’affectation à un EditForm.Model ou à un EditForm.EditContext peut lier un formulaire à des données.

Liaison de données

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

Remarque

La plupart des exemples de modèles de formulaire de cet article lient des formulaires aux propriétés C#, mais la liaison de champ C# est également prise en charge.

Liaison du contexte

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

Attribuez soit un EditContext ou un Model à un EditForm. Si les deux sont affectés, une erreur d’exécution est levée.

Types pris en charge

La liaison prend en charge :

  • Types primitifs
  • Collections
  • Types complexes
  • Types récursifs
  • Types avec constructeurs
  • Enums

Vous pouvez également utiliser les attributs [DataMember] et [IgnoreDataMember] pour personnaliser la liaison de données. Utilisez ces attributs pour renommer, ignorer et marquer les propriétés en fonction des besoins.

Options supplémentaires de liaison

Des options supplémentaires de liaison de données sont disponibles auprès de RazorComponentsServiceOptions lors de l’appel à AddRazorComponents :

Les valeurs par défaut affectées par l’infrastructure sont les suivantes :

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

Noms de formulaires

Utilisez le paramètre FormName pour attribuer un nom du formulaire. Les noms de formulaires doivent être uniques pour lier des données du modèle. Le formulaire suivant est nommé RomulanAle :

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

Ajout d’un nom de formulaire :

  • Est nécessaire pour tous les formulaires soumis par des composants côté serveur rendus statiquement.
  • N’est pas nécessaire pour les formulaires soumis par des composants rendus interactivement, ce qui inclut les formulaires dans des applications et des composants Blazor WebAssembly avec un mode de rendu interactif. Nous vous recommandons cependant de fournir un nom du formulaire unique pour chaque formulaire afin d’éviter les erreurs de publication si l’interactivité était supprimée pour un formulaire.

Le nom du formulaire est vérifie seulement quand le formulaire est publié sur un point de terminaison sous la forme d’une requête HTTP POST traditionnelle depuis un composant côté serveur rendu statiquement. L’infrastructure ne lève aucune exception au moment du rendu d’un formulaire, mais uniquement lorsqu’une requête HTTP POST arrive sans spécifier de nom du formulaire.

Il existe une étendue de formulaires non nommés (chaîne vide) au-dessus du composant racine de l’application, ce qui suffit quand il n’y a pas de collisions de noms de formulaire dans l’application. Si des collisions de noms de formulaire sont possibles, par exemple quand vous incluez un formulaire depuis une bibliothèque et que vous n’avez aucun contrôle sur le nom de formulaire utilisé par le développeur de la bibliothèque, fournissez une étendue de noms de formulaires avec le composant FormMappingScope dans le projet principal de l’Blazor Web App.

Dans l’exemple suivant, le composant HelloFormFromLibrary a un formulaire nommé Hello et se trouve dans une bibliothèque.

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

Le composant NamedFormsWithScope suivant utilise le composant HelloFormFromLibrary de la bibliothèque et a aussi un formulaire nommé Hello. Le nom de l’étendue du composant FormMappingScope est ParentContext pour les formulaires fournis par le composant HelloFormFromLibrary. Bien que les deux formulaires de cet exemple aient le même nom de formulaire (Hello), les noms de formulaire n’entrent pas en collision et les événements sont routés vers le formulaire approprié pour les événements POST des formulaires.

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

Fournir un paramètre à partir du formulaire ([SupplyParameterFromForm])

L’attribut [SupplyParameterFromForm] indique que la valeur de la propriété associée doit être fournie à partir des données du formulaire. Les données de la requête, correspondant au nom de la propriété, sont liées à la propriété. Les entrées, basées sur InputBase<TValue>, génèrent des noms de valeur de formulaire correspondant aux noms Blazor utilisés pour la liaison de modèle. Contrairement aux propriétés des paramètres de composant ([Parameter]), les propriétés annotées avec [SupplyParameterFromForm] ne doivent pas nécessairement être marquées public.

Vous pouvez spécifier les paramètres de liaison de formulaire suivants à l'[SupplyParameterFromForm]attribut :

  • Name : Obtient ou définit le nom du paramètre. Le nom sert à déterminer le préfixe à utiliser pour correspondre aux données du formulaire et décider de lier (ou pas) la valeur.
  • FormName : Obtient ou définit le nom du gestionnaire. Le nom est utilisé pour faire correspondre le paramètre au formulaire par le nom du formulaire, afin de décider de lier (ou pas) la valeur.

L’exemple suivant lie indépendamment deux formulaires à leurs modèles par le nom du formulaire.

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

Imbriquer et lier des formulaires

Les instructions suivantes montrent comment imbriquer et lier des formulaires enfants.

La classe de détails du navire (ShipDetails) suivante contient une description et une longueur d’un sous-formulaire.

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 Ship suivante nomme un identificateur (Id) et inclut les détails du navire.

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

Le sous-formulaire suivant sert à modifier les valeurs du type ShipDetails. Cette opération est implémentée en héritant d’un Editor<T> au-dessus du composant. Editor<T> garantit que le composant enfant génère les noms de champs de formulaire appropriés en fonction du modèle (T), avec T qui a pour valeur ShipDetails dans l’exemple suivant.

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>

Le formulaire principal est lié à la classe Ship. Le composant StarshipSubform est utilisé pour modifier les détails du navire, liés en tant que 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);
}

Initialiser des données de formulaire avec un SSR statique

Lorsqu’un composant adopte un SSR statique, la méthode de cycle de vie OnInitialized{Async} et la méthode de cycle de vie OnParametersSet{Async} se déclenchent lorsque le composant est initialement rendu et sur chaque formulaire POST sur le serveur. Pour initialiser des valeurs de modèle de formulaire, vérifiez si le modèle possède déjà des données avant d’affecter de nouvelles valeurs de modèle OnParametersSet{Async}, comme l’illustre l’exemple suivant.

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

Scénarios avancés d’erreur de mappage du formulaire

L’infrastructure instancie et remplit le FormMappingContext pour un formulaire, ce qui est le contexte associé à l’opération de mappage d’un formulaire donné. Chaque étendue de mappage (définie par un composant FormMappingScope) instancie FormMappingContext. Chaque fois qu’un [SupplyParameterFromForm] demande le contexte d’une valeur, l’infrastructure remplit le FormMappingContext par la valeur essayée et toutes les erreurs de mappage.

Les développeurs ne sont pas censés interagir directement avec FormMappingContext, car il s’agit principalement d’une source de données pour InputBase<TValue>, EditContext et d’autres implémentations internes pour afficher les erreurs de mappage en tant qu’erreurs de validation. Dans les scénarios personnalisés avancés, les développeurs peuvent accéder directement à FormMappingContext en tant que [CascadingParameter] pour écrire du code personnalisé qui consomme les valeurs essayées et les erreurs de mappage.

Composants d’entrée personnalisés

Pour les scénarios de traitement d’entrée personnalisés, les sous-sections suivantes illustrent les composants d’entrée personnalisés :

Nous vous recommandons de dériver vos composants d’entrée personnalisés de InputBase<TValue>, sauf si des exigences spécifiques vous empêchent de le faire. La classe InputBase<TValue> est activement gérée par l’équipe ASP.NET Core, afin d’assurer qu’elle reste à jour avec les dernières fonctionnalités et modifications du framework Blazor.

Composant d’entrée basé sur InputBase<T>

L’exemple de composant suivant :

  • Hérite de InputBase<TValue>. Les composants qui héritent de InputBase<TValue> doivent être utilisés dans un formulaire Blazor (EditForm).
  • Prend une entrée booléenne en fonction d’une case à cocher.
  • Définit la couleur d’arrière-plan de son conteneur <div> en fonction de l’état de la case à cocher, ce qui se produit lorsque la méthode AfterChange s’exécute après la liaison (@bind:after).
  • Est nécessaire pour remplacer la méthode TryParseValueFromString de la classe de base, mais ne traite pas les données d’entrée de chaîne, car une case à cocher ne fournit pas de données de chaîne. Des exemples d’implémentations de TryParseValueFromString pour d’autres types de composants d’entrée qui traitent les entrées de chaîne sont disponibles dans la source de référence ASP.NET Core.

Remarque

Les liens de documentation vers la source de référence .NET chargent généralement la branche par défaut du référentiel, qui représente le développement actuel pour la prochaine version de .NET. Pour sélectionner une balise pour une version spécifique, utilisez la liste déroulante Échanger les branches ou les balises. Pour plus d’informations, consultez Comment sélectionner une balise de version du code source 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)}'.");
}

Pour utiliser le composant précédent dans le formulaire d’exemple de vaisseau (Starship3.razor/Starship.cs), remplacez le bloc <div> du champ d’approbation d’ingénierie par une instance de composant EngineeringApprovalInputDerived liée à la propriété IsValidatedDesign du modèle :

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

Composant d’entrée avec contrôle développeur complet

L’exemple de composant suivant :

  • N’hérite pas de InputBase<TValue>. Le composant prend le contrôle total du traitement des entrées, notamment la liaison, les rappels et la validation. Le composant peut être utilisé à l’intérieur ou en dehors d’un formulaire Blazor (EditForm).
  • Prend une entrée booléenne en fonction d’une case à cocher.
  • Modifie la couleur d’arrière-plan si la case à cocher est cochée.

Le code du composant inclut :

  • La propriété Value est utilisée avec une liaison bidirectionnelle pour obtenir ou définir la valeur de l’entrée. ValueChanged est le rappel qui met à jour la valeur liée.

  • Lors d’une utilisation dans un formulaire Blazor :

    • Le EditContext est une valeur en cascade.
    • fieldCssClass affecte le champ en fonction du résultat de la validation EditContext.
    • ValueExpression est une expression (Expression<Func<T>>) affectée par l’infrastructure qui identifie la valeur liée.
    • FieldIdentifier identifie de manière unique un champ unique qui peut être modifié, et correspondant généralement à une propriété de modèle. L’identifiant de champ est créé avec l’expression qui identifie la valeur liée (ValueExpression).
  • Dans le gestionnaire d’événements OnChange :

    • La valeur de l’entrée de case à cocher est obtenue à partir de InputFileChangeEventArgs.
    • La couleur d’arrière-plan et la couleur de texte de l’élément <div> conteneur sont définies.
    • EventCallback.InvokeAsync appelle le délégué associé à la liaison et envoie une notification d’événement aux consommateurs pour leur signaler que la valeur a changé.
    • Si le composant est utilisé dans un EditForm (la propriété EditContext n’est pas null), EditContext.NotifyFieldChanged est appelé pour déclencher la validation.

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

Pour utiliser le composant précédent dans le formulaire d’exemple de vaisseau (Starship3.razor/Starship.cs), remplacez le bloc <div> du champ d’approbation d’ingénierie par une instance de composant EngineeringApprovalInputStandalone liée à la propriété IsValidatedDesign du modèle :

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

Le composant EngineeringApprovalInputStandalone est également fonctionnel en dehors d’un EditForm:

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

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

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

Cases d’option

L’exemple de cette section est basé sur le formulaire Starfleet Starship Database (composant Starship3) de la section Exemple de formulaire de cet article.

Ajoutez les types enum suivants à l’application. Créez un nouveau fichier pour les contenir ou ajoutez-les au fichier 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 }
}

Rendre la classe ComponentEnums accessible au :

  • Modèle Starship dans Starship.cs (par exemple using static ComponentEnums;).
  • Formulaire Starfleet Starship Database (Starship3.razor) (par exemple @using static ComponentEnums).

Utilisez des composants InputRadio<TValue> avec le composant InputRadioGroup<TValue> pour créer un groupe de cases d’option. Dans l’exemple suivant, des propriétés sont ajoutées au modèle Starship décrit dans la section Exemple de formulaire de l’article Composants d’entrée :

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

Mettez à jour le formulaire Starfleet Starship Database ( composant Starship3) de la section Exemple de formulaire de l’article Composants d’entrée. Ajoutez les composants à produire :

  • Un groupe de cases d’option pour le fabricant du navire.
  • Un groupe de cases d’option imbriquées pour la couleur du moteur et du navire.

Remarque

Les groupes de cases d’option imbriquées ne sont pas souvent utilisés dans les formulaires, car ils peuvent entraîner une disposition désorganisée des contrôles de formulaire pouvant perturber les utilisateurs. Toutefois, dans certains cas, elles ont du sens dans la conception de l’interface utilisateur, comme dans l’exemple suivant qui associe des recommandations pour deux entrées utilisateur, le moteur du navire et la couleur du navire. Un moteur et une couleur sont requis par la validation du formulaire. La disposition du formulaire utilise des InputRadioGroup<TValue> imbriquées pour coupler les recommandations de moteur et de couleur. Toutefois, l’utilisateur peut combiner n’importe quel moteur avec n’importe quelle couleur pour envoyer le formulaire.

Remarque

Assurez-vous de rendre la classe ComponentEnums disponible pour le composant de l’exemple suivant :

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

Remarque

Si Name est omis, les composants InputRadio<TValue> sont regroupés par leur ancêtre le plus récent.

Si vous avez implémenté le balisage Razor précédent dans le composant Starship3 de la section Exemple de formulaire de l’article Composants d’entrée, mettez à jour la journalisation pour la méthode 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);

Lorsque vous utilisez des cases d’option dans un formulaire, la liaison de données est gérée différemment des autres éléments, car les cases d’option sont évaluées en tant que groupe. La valeur de chaque case d’option est fixe, mais la valeur du groupe de cases d’option est la valeur de la case d’option sélectionnée. L’exemple suivant montre comment vous :

  • Gérez la liaison de données pour un groupe de cases d’option.
  • Prise en charge de la validation à l’aide d’un composant InputRadio<TValue> personnalisé.

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

Pour plus d’informations sur les paramètres de type générique (@typeparam), consultez les articles suivants :

Utilisez l’exemple de modèle suivant.

StarshipModel.cs :

using System.ComponentModel.DataAnnotations;

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

Le composant RadioButtonExample suivant utilise le composant InputRadio précédent pour obtenir et valider une évaluation de l’utilisateur :

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