Teilen über


ASP.NET Core Blazor-Formularbindung

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Warnung

Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der Supportrichtlinie für .NET und .NET Core. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

In diesem Artikel wird erläutert, wie die Bindung in Blazor-Formularen verwendet wird.

EditForm/EditContext Modell

EditForm erstellt einen EditContext auf Grundlage des zugewiesenen Objekts als kaskadierenden Wert für andere Komponenten im Formular. Der EditContext verfolgt Metadaten über den Bearbeitungsprozess nach, einschließlich der Formularfelder, die geändert wurden, und der aktuellen Validierungsnachrichten. Durch das Zuweisen zu einem EditForm.Model oder einem EditForm.EditContext lässt sich ein Formular an Daten binden.

Modellbindung

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

Hinweis

Die meisten Formularmodellbeispielen dieses Artikels binden Formulare an C#-Eigenschaften, die C#-Feldbindung wird jedoch ebenfalls unterstützt.

Kontextbindung

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

Weisen Sie einem EditForm entweder einen EditContext oder ein Model zu. Wenn beide zugewiesen sind, wird ein Laufzeitfehler ausgelöst.

Unterstützte Typen

Die Bindung unterstützt Folgendes:

  • Primitive Typen
  • Auflistungen
  • Komplexe Typen
  • Rekursive Typen
  • Typen mit Konstruktoren
  • Enumerationen

Sie können auch die Attribute [DataMember] und [IgnoreDataMember] verwenden, um die Modellbindung anzupassen. Verwenden Sie diese Attribute, um Eigenschaften nach Bedarf umzubenennen, zu ignorieren und zu markieren.

Zusätzliche Bindungsoptionen

Zusätzliche Modellbindungsoptionen stehen in RazorComponentsServiceOptions zur Verfügung, wenn AddRazorComponents aufgerufen wird:

Im Folgenden werden die vom Framework zugewiesenen Standardwerte veranschaulicht:

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

Formularnamen

Verwenden Sie den Parameter FormName, um einen Formularnamen zuzuweisen. Formularnamen müssen eindeutig sein, um Modelldaten zu binden. Das folgende Formular heißt RomulanAle:

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

Für die Bereitstellung eines Formularnamens gilt:

  • Ist für alle Formulare erforderlich, die von statisch gerenderten serverseitigen Komponenten übermittelt werden.
  • Ist nicht erforderlich für Formulare, die von interaktiv gerenderten Komponenten übermittelt werden, die Formulare in Blazor WebAssembly-Apps und Komponenten mit einem interaktiven Rendermodus enthalten. Es wird jedoch empfohlen, für jedes Formular einen eindeutigen Formularnamen anzugeben, um Formularveröffentlichungsfehler zur Laufzeit zu verhindern, wenn die Interaktivität für ein Formular verworfen wird.

Der Formularname wird nur überprüft, wenn das Formular als herkömmliche HTTP POST-Anforderung von einer statisch gerenderten serverseitigen Komponente an einen Endpunkt übermittelt wird. Das Framework löst eine Ausnahme nicht zum Zeitpunkt des Renderns eines Formulars, sondern erst zu dem Zeitpunkt aus, an dem eine HTTP POST-Anforderung eintrifft. Es gibt keinen Formularnamen an.

Es gibt einen unbenannten (leere Zeichenfolge) Formularbereich oberhalb der Stammkomponente der App, der ausreicht, wenn es in der App keine Kollisionen mit Formularnamen gibt. Wenn Konflikte bei Formularnamen möglich sind, z. B. wenn Sie ein Formular aus einer Bibliothek einschließen und keine Kontrolle über den von den Entwickelnden der Bibliothek verwendeten Formularnamen haben, geben Sie mit der FormMappingScope-Komponente im Hauptprojekt der Blazor Web App einen Formularnamenbereich an.

Im folgenden Beispiel hat die HelloFormFromLibrary-Komponente ein Formular namens Hello und befindet sich in einer Bibliothek.

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

Die folgende NamedFormsWithScope-Komponente verwendet die HelloFormFromLibrary-Komponente der Bibliothek, und sie verfügt über ein Formular mit dem Namen Hello. Der Bereichsname der FormMappingScope-Komponente lautet ParentContext für alle Formulare, die von der HelloFormFromLibrary-Komponente bereitgestellt werden. Obwohl beide Formulare in diesem Beispiel denselben Formularnamen aufweisen (Hello), tritt kein Konflikt bei den Formularnamen auf, und die Ereignisse werden für POST-Ereignisse an das richtige Formular weitergeleitet.

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

Angeben eines Parameters aus dem Formular ([SupplyParameterFromForm])

Das Attribut [SupplyParameterFromForm] gibt an, dass der Wert der zugeordneten Eigenschaft aus den Formulardaten für das Formular bereitgestellt werden soll. Daten in der Anforderung, die dem Namen der Eigenschaft entsprechen, werden an die Eigenschaft gebunden. Auf InputBase<TValue> basierende Eingaben generieren Formularwertnamen, die den Namen entsprechen, die Blazor für die Modellbindung verwendet. Im Gegensatz zu den Eigenschaften von Komponentenparametern ([Parameter]) ist es bei den mit [SupplyParameterFromForm] kommentierten Eigenschaften nicht erforderlich, sie mit public zu kennzeichnen.

Sie können die folgenden Formularbindungsparameter für das [SupplyParameterFromForm]-Attribut angeben:

  • Name: Ruft den Namen für den Parameter ab oder legt ihn fest. Der Name wird verwendet, um das Präfix zu bestimmen, das für den Abgleich mit den Formulardaten verwendet werden soll, und um zu entscheiden, ob der Wert gebunden werden muss.
  • FormName: Ruft den Namen für den Handler ab oder legt ihn fest. Der Name wird für den Abgleich des Parameters mit dem Formular anhand des Formularnamens verwendet, um zu entscheiden, ob der Wert gebunden werden muss.

Das folgende Beispiel bindet zwei Formulare unabhängig voneinander anhand des Formularnamens an ihre Modelle.

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

Schachteln und Binden von Formularen

Die folgende Anleitung veranschaulicht, wie untergeordnete Formulare geschachtelt und gebunden werden.

Die folgende Seite mit Schiffsdetails (ShipDetails) enthält eine Beschreibung und eine Länge für ein Unterformular.

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

Die folgende Ship-Klasse benennt einen Bezeichner (Id) und enthält die Schiffsdetails.

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

Das folgende Unterformular wird zum Bearbeiten von Werten vom Typ ShipDetails verwendet. Dies wird durch Vererben von Editor<T> oben in der Komponente implementiert. Editor<T> stellt sicher, dass die untergeordnete Komponente die richtigen Formularfeldnamen basierend auf dem Modell (T) generiert, wobei T im folgenden Beispiel ShipDetails lautet.

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>

Das Hauptformular ist an die Ship-Klasse gebunden. Die Komponente StarshipSubform wird verwendet, um Lieferdetails zu bearbeiten, die als Model!.Details gebunden sind.

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

Initialisieren von Formulardaten mit statischem SSR

Wenn eine Komponente statisches SSR verwendet, werden die OnInitialized{Async}-Lebenszyklus-Methode und die OnParametersSet{Async}-Lebenszyklus-Methode beim ersten Rendern der Komponente und bei jedem POST-Vorgang des Formulars an den Server ausgelöst. Um die Werte des Formularmodells zu initialisieren, überprüfen Sie, ob das Modell bereits über Daten verfügt, bevor Sie neue Modellwerte in OnParametersSet{Async} zuweisen, wie im folgenden Beispiel gezeigt.

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

Erweiterte Fehlerszenarien bei der Formularzuordnung

Das Framework instanziiert den FormMappingContext für ein Formular und füllt ihn auf. Dies ist der Kontext, der mit dem Zuordnungsvorgang eines bestimmten Formulars verknüpft ist. Jeder Zuordnungsbereich (definiert durch eine FormMappingScope-Komponente) instanziiert FormMappingContext. Jedes Mal, wenn ein [SupplyParameterFromForm] den Kontext nach einem Wert abfragt, füllt das Framework den FormMappingContext mit dem versuchten Wert und allen Zuordnungsfehlern auf.

Entwickler*innen interagieren wahrscheinlich nicht direkt mit FormMappingContext, da es sich hauptsächlich um eine Datenquelle für InputBase<TValue>, EditContext und andere interne Implementierungen handelt, um Zuordnungsfehler als Validierungsfehler anzuzeigen. In erweiterten benutzerdefinierten Szenarien können Entwickler*innen direkt als [CascadingParameter] auf FormMappingContext zugreifen, um benutzerdefinierten Code zu schreiben, der die versuchten Werte und Fehlerzuordnungen verwendet.

Benutzerdefinierte Eingabekomponenten

Für benutzerdefinierte Eingabeverarbeitungsszenarien werden in den folgenden Unterabschnitten benutzerdefinierte Eingabekomponenten vorgestellt:

Es wird empfohlen, ihre benutzerdefinierten Eingabekomponenten von InputBase<TValue> abzuleiten, es sei denn, bestimmte Anforderungen verhindern dies. Die InputBase<TValue>-Klasse wird aktiv vom ASP.NET Core-Team verwaltet, um sicherzustellen, dass sie mit den neuesten Blazor-Features und Frameworkänderungen auf dem neuesten Stand bleibt.

Eingabekomponente basierend auf InputBase<T>

Für die folgende Beispielkomponente gilt Folgendes:

  • Erbt von InputBase<TValue>. Komponenten, die von InputBase<TValue> erben, müssen in einer Blazor-Form (EditForm) verwendet werden.
  • Verwendet boolesche Eingaben aus einem Kontrollkästchen.
  • Legt die Hintergrundfarbe seines Containers <div> auf der Grundlage des Zustands des Kontrollkästchens fest, der auftritt, wenn die Methode AfterChange nach dem Verknüpfen ausgeführt wird (@bind:after).
  • Ist erforderlich, um die TryParseValueFromString-Methode der Basisklasse zu überschreiben, verarbeitet aber keine Zeichenfolge-Eingabedaten, da ein Ankreuzfeld keine Zeichenfolge-Daten bereitstellt. Beispielimplementierungen von TryParseValueFromString für andere Arten von Eingabekomponenten, die Zeichenfolgen verarbeiten, sind in der ASP.NET Core-Referenzquelle verfügbar.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (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)}'.");
}

Um die vorangehende Komponente in der Starship-Beispielform (Starship3.razor/Starship.cs) zu verwenden, ersetzen Sie den <div>-Block für das technische Genehmigungsfeld durch eine EngineeringApprovalInputDerived-Komponenteninstanz, die mit der IsValidatedDesign-Eigenschaft des Modells verknüpft ist:

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

Eingabekomponente mit vollständiger Entwicklersteuerung

Für die folgende Beispielkomponente gilt Folgendes:

  • Erbt nicht von InputBase<TValue>. Die Komponente übernimmt die vollständige Kontrolle über die Eingabeverarbeitung, einschließlich der Verknüpfung, der Rückrufe und der Validierung. Die Komponente kann innerhalb oder außerhalb einer Blazor-Form (EditForm) verwendet werden.
  • Verwendet boolesche Eingaben aus einem Kontrollkästchen.
  • Ändert die Hintergrundfarbe, wenn das Kontrollkästchen angekreuzt wurde.

Code in der Komponente umfasst:

  • Die Eigenschaft Value wird bei der zweiseitigen Verknüpfung verwendet, um den Wert der Eingabe zu erhalten oder festzulegen. ValueChanged ist der Callback, der den verknüpften Wert aktualisiert.

  • Bei Verwendung in einer Blazor-Form:

    • Das EditContext ist ein kaskadierender Wert.
    • fieldCssClass formatiert das Feld auf der Grundlage des Ergebnisses der EditContext-Überprüfung.
    • ValueExpression ist ein Ausdruck (Expression<Func<T>>), der vom Framework zugewiesen wird, der den gebundenen Wert identifiziert.
    • FieldIdentifier identifiziert eindeutig ein einzelnes Feld, das bearbeitet werden kann und normalerweise einer Modelleigenschaft entspricht. Der Feldbezeichner wird mit dem Ausdruck erstellt, der den verknüpften Wert identifiziert (ValueExpression).
  • In der OnChange Ereignisbehandlung:

    • Der Wert der Checkbox-Eingabe ergibt sich aus InputFileChangeEventArgs.
    • Die Hintergrundfarbe und die Textfarbe des Container-Elements <div> werden festgelegt.
    • EventCallback.InvokeAsync ruft den mit der Verknüpfung verbundenen Delegaten auf und sendet eine Ereignisbenachrichtigung an die Verbraucher, dass sich der Wert geändert hat.
    • Wenn die Komponente in einem EditForm verwendet wird (die EditContext-Eigenschaft ist nicht null), wird EditContext.NotifyFieldChanged aufgerufen, um die Validierung auszulösen.

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

Um die vorangehende Komponente im Sternenschiff-Beispielformular (Starship3.razor/Starship.cs) zu verwenden, ersetzen Sie den <div>-Block für das technische Genehmigungsfeld durch eine EngineeringApprovalInputStandalone-Komponenteninstanz, die mit der IsValidatedDesign-Eigenschaft des Modells verknüpft ist:

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

Die EngineeringApprovalInputStandalone-Komponente hat auch außerhalb eines EditForm eine Funktion:

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

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

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

Optionsfelder

Das Beispiel in diesem Abschnitt basiert auf dem Starfleet Starship Database-Formular (Starship3-Komponente) des Abschnitts Beispielformular in diesem Artikel.

Fügen Sie der App die folgenden enum-Typen hinzu. Erstellen Sie eine neue Datei, um diese aufzunehmen, oder fügen Sie sie der Datei Starship.cs hinzu.

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

Machen Sie die ComponentEnums-Klasse für Folgendes zugänglich:

  • Starship-Modell in Starship.cs (z. B. using static ComponentEnums;).
  • Starfleet Starship Database-Formular (Starship3.razor) (z. B. @using static ComponentEnums).

Verwenden Sie InputRadio<TValue>-Komponenten mit der InputRadioGroup<TValue>-Komponente, um eine Optionsfeldgruppe zu erstellen. Im folgenden Beispiel werden dem im Abschnitt Beispielformular des Artikels Eingabekomponenten beschriebenen Starship-Modell Eigenschaften hinzugefügt:

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

Aktualisieren Sie das Starfleet Starship Database-Formular (Starship3-Komponente) im Abschnitt Beispielformular des Artikels Eingabekomponenten. Fügen Sie die Komponenten hinzu, damit Folgendes erstellt wird:

  • Eine Optionsfeldgruppe für den Schiffshersteller.
  • Eine geschachtelte Optionsfeldgruppe für den Motor und die Farbe des Schiffs.

Hinweis

Geschachtelte Optionsfeldgruppen werden nicht häufig in Formularen verwendet, weil sie zu einem unorganisierten Layout von Formularsteuerelementen führen können, das Benutzer verwirren kann. Es gibt jedoch Fälle, in denen sie im Benutzeroberflächenentwurf sinnvoll sind, z. B. im folgenden Beispiel, bei dem Empfehlungen für zwei Benutzereingaben (Schiffsmotor (ship enginge) und Schiffsfarbe (ship color)) gepaart werden. Für die Validierung des Formulars ist ein Motor und eine Farbe erforderlich. Das Layout des Formulars verwendet geschachtelte InputRadioGroup<TValue>s, um Empfehlungen für Motor und Farbe zu paaren. Der Benutzer kann allerdings jeden Motor mit jeder Farbe kombinieren, um das Formular zu übermitteln.

Hinweis

Stellen Sie sicher, dass die ComponentEnums-Klasse für die Komponente für das folgende Beispiel verfügbar ist:

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

Hinweis

Wenn Name ausgelassen wird, werden die InputRadio<TValue>-Komponenten nach dem direkten Vorgänger gruppiert.

Wenn Sie das vorangehende Razor-Markup in der Starship3-Komponente des Abschnitts Beispielformular des Artikels Eingabekomponenten implementiert haben, aktualisieren Sie die Protokollierung für die Submit-Methode:

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

Beim Verwenden von Optionsfeldern in einem Formular werden Datenbindungen nicht wie andere Elemente verarbeitet, da Optionsfelder als Gruppe ausgewertet werden. Der Wert der einzelnen Optionsfelder ist fest. Der Wert der Optionsfeldgruppe ist jedoch der Wert des ausgewählten Optionsfelds. Das folgende Beispiel veranschaulicht die Vorgehensweise:

  • Verarbeiten von Datenbindungen für eine Optionsfeldgruppe
  • Unterstützen der Validierung mithilfe einer benutzerdefinierten InputRadio<TValue>-Komponente

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

Weitere Informationen zu den generischen Typparametern (@typeparam) finden Sie in den folgenden Artikeln:

Verwenden Sie das folgende Beispielmodell.

StarshipModel.cs:

using System.ComponentModel.DataAnnotations;

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

Die folgende RadioButtonExample-Komponente verwendet die vorangehende InputRadio-Komponente, um eine Bewertung vom Benutzer zu erhalten und sie zu überprüfen:

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