ASP.NET vazby základních Blazor formulářů

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Důležité

Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.

Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Tento článek vysvětluje, jak používat vazbu ve Blazor formulářích.

EditForm/EditContext model

EditContext Vytvoří EditForm na základě přiřazeného objektu kaskádovou hodnotu pro ostatní komponenty ve formuláři. Sleduje EditContext metadata procesu úprav, včetně toho, která pole formuláře byla změněna, a aktuální ověřovací zprávy. Přiřazení k formuláři EditForm.Model nebo k EditForm.EditContext datům může vytvořit vazbu.

Vazby modelu

Přiřazení:EditForm.Model

<EditForm ... Model="Model" ...>
    ...
</EditForm>

@code {
    [SupplyParameterFromForm]
    public Starship? Model { get; set; }

    protected override void OnInitialized() => Model ??= new();
}
<EditForm ... Model="Model" ...>
    ...
</EditForm>

@code {
    public Starship? Model { get; set; }

    protected override void OnInitialized() => Model ??= new();
}

Poznámka:

Většina příkladů modelu formulářů tohoto článku spojuje formuláře s vlastnostmi jazyka C#, ale vazba pole jazyka C# je podporována také.

Kontextová vazba

Přiřazení:EditForm.EditContext

<EditForm ... EditContext="editContext" ...>
    ...
</EditForm>

@code {
    private EditContext? editContext;

    [SupplyParameterFromForm]
    public Starship? Model { get; set; }

    protected override void OnInitialized()
    {
        Model ??= new();
        editContext = new(Model);
    }
}
<EditForm ... EditContext="editContext" ...>
    ...
</EditForm>

@code {
    private EditContext? editContext;

    public Starship? Model { get; set; }

    protected override void OnInitialized()
    {
        Model ??= new();
        editContext = new(Model);
    }
}

Přiřaďte k ModelEditContextobjektu .EditForm Pokud jsou oba přiřazené, vyvolá se chyba za běhu.

Podporované typy

Vazba podporuje:

  • Primitivní typy
  • Kolekce
  • Komplexní typy
  • Rekurzivní typy
  • Typy s konstruktory
  • Výčty

Vazby [DataMember][IgnoreDataMember] modelu můžete přizpůsobit také pomocí atributů. Pomocí těchto atributů můžete přejmenovat vlastnosti, ignorovat vlastnosti a označit vlastnosti jako povinné.

Další možnosti vazby

Při volání AddRazorComponentsjsou k dispozici RazorComponentsServiceOptions další možnosti vazby modelu:

Následující příklad ukazuje výchozí hodnoty přiřazené architekturou:

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

Názvy formulářů

Pomocí parametru FormName přiřaďte název formuláře. Názvy formulářů musí být jedinečné pro vytvoření vazby dat modelu. Následující formulář má název RomulanAle:

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

Zadání názvu formuláře:

  • Vyžaduje se pro všechny formuláře odeslané staticky vykreslenými komponentami na straně serveru.
  • Nevyžaduje se u formulářů odesílaných interaktivně vykreslenými komponentami, které zahrnují formuláře v Blazor WebAssembly aplikacích a komponentách s interaktivním režimem vykreslování. Doporučujeme však zadat jedinečný název formuláře pro každý formulář, aby se zabránilo chybám při publikování za běhu formuláře, pokud dojde k vyřazení interaktivity formuláře.

Název formuláře je kontrolován pouze v případech, kdy se formulář publikuje do koncového bodu jako tradiční požadavek HTTP POST ze staticky vykreslené součásti na straně serveru. Architektura nevyvolá výjimku v okamžiku vykreslení formuláře, ale pouze v okamžiku, kdy přijde http POST a nezadá název formuláře.

Ve výchozím nastavení je nad kořenovou komponentou aplikace obor formuláře bez názvu (prázdný řetězec), který stačí, když v aplikaci nedojde ke kolizi názvu formuláře. Pokud jsou možné kolize názvů formulářů, například při zahrnutí formuláře z knihovny a nemáte žádnou kontrolu nad názvem formuláře používaným vývojářem knihovny, zadejte obor názvu formuláře s FormMappingScope komponentou v Blazor hlavním projektu webové aplikace.

V následujícím příkladu HelloFormFromLibrary má komponenta název Hello formuláře a je v knihovně.

HelloFormFromLibrary.razor:

<EditForm FormName="Hello" Model="this" OnSubmit="Submit">
    <InputText @bind-Value="Name" />
    <button type="submit">Submit</button>
</EditForm>

@if (submitted)
{
    <p>Hello @Name from the library's form!</p>
}

@code {
    bool submitted = false;

    [SupplyParameterFromForm]
    public string? Name { get; set; }

    private void Submit() => submitted = true;
}

Následující NamedFormsWithScope komponenta používá komponentu knihovny HelloFormFromLibrary a má také formulář s názvem Hello. Název FormMappingScope oboru komponenty je ParentContext určen pro všechny formuláře zadané komponentou HelloFormFromLibrary . I když oba formuláře v tomto příkladu mají název formuláře (Hello), názvy formulářů nejsou kolidovány a události jsou směrovány do správného formuláře pro události POST formuláře.

NamedFormsWithScope.razor:

@page "/named-forms-with-scope"

<div>Hello form from a library</div>

<FormMappingScope Name="ParentContext">
    <HelloFormFromLibrary />
</FormMappingScope>

<div>Hello form using the same form name</div>

<EditForm FormName="Hello" Model="this" OnSubmit="Submit">
    <InputText @bind-Value="Name" />
    <button type="submit">Submit</button>
</EditForm>

@if (submitted)
{
    <p>Hello @Name from the app form!</p>
}

@code {
    bool submitted = false;

    [SupplyParameterFromForm]
    public string? Name { get; set; }

    private void Submit() => submitted = true;
}

Zadání parametru z formuláře ([SupplyParameterFromForm])

Atribut [SupplyParameterFromForm] označuje, že hodnota přidružené vlastnosti by měla být zadána z dat formuláře pro formulář. Data v požadavku, která odpovídají názvu vlastnosti, jsou svázaná s vlastností. Vstupy založené na InputBase<TValue> generování názvů hodnot formuláře, které odpovídají názvům Blazor , které se používají pro vazbu modelu.

Pro atribut můžete zadat následující parametry vazby [SupplyParameterFromForm] formuláře:

  • Name: Získá nebo nastaví název parametru. Název se používá k určení předpony, která se má použít ke shodě dat formuláře, a rozhodnutí, zda má být hodnota vázána nebo ne.
  • FormName: Získá nebo nastaví název obslužné rutiny. Název se používá ke shodě parametru s formulářem podle názvu formuláře, aby se rozhodl, zda je potřeba hodnotu vázat nebo ne.

Následující příklad nezávisle vytvoří vazbu dvou formulářů na jejich modely podle názvu formuláře.

Starship6.razor:

@page "/starship-6"
@inject ILogger<Starship6> Logger

<EditForm Model="Model1" OnSubmit="Submit1" FormName="Holodeck1">
    <div>
        <label>
            Holodeck 1 Identifier: 
            <InputText @bind-Value="Model1!.Id" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

<EditForm Model="Model2" OnSubmit="Submit2" FormName="Holodeck2">
    <div>
        <label>
            Holodeck 2 Identifier: 
            <InputText @bind-Value="Model2!.Id" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

@code {
    [SupplyParameterFromForm(FormName = "Holodeck1")]
    public Holodeck? Model1 { get; set; }

    [SupplyParameterFromForm(FormName = "Holodeck2")]
    public Holodeck? Model2 { get; set; }

    protected override void OnInitialized()
    {
        Model1 ??= new();
        Model2 ??= new();
    }

    private void Submit1()
    {
        Logger.LogInformation("Submit1: Id = {Id}", Model1?.Id);
    }

    private void Submit2()
    {
        Logger.LogInformation("Submit2: Id = {Id}", Model2?.Id);
    }

    public class Holodeck
    {
        public string? Id { get; set; }
    }
}

Vnoření a vytvoření vazby formulářů

Následující doprovodné materiály ukazují, jak vnořit a svázat podřízené formuláře.

Následující třída podrobností o expediciShipDetails obsahuje popis a délku podformulář.

ShipDetails.cs:

namespace BlazorSample;

public class ShipDetails
{
    public string? Description { get; set; }
    public int? Length { get; set; }
}

Následující Ship třída pojmenuje identifikátor (Id) a obsahuje podrobnosti o expedici.

Ship.cs:

namespace BlazorSample
{
    public class Ship
    {
        public string? Id { get; set; }
        public ShipDetails Details { get; set; } = new();
    }
}

Následující podformulář slouží k úpravě hodnot ShipDetails typu. To je implementováno děděním Editor<T> v horní části komponenty. Editor<T> zajišťuje, aby podřízená komponenta vygenerovala správné názvy polí formuláře na základě modelu (T), kde T v následujícím příkladu je 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>

Hlavní formulář je vázán na Ship třídu. Komponenta StarshipSubform slouží k úpravě podrobností o expedici svázané jako Model!.Details.

Starship7.razor:

@page "/starship-7"
@inject ILogger<Starship7> Logger

<EditForm Model="Model" OnSubmit="Submit" FormName="Starship7">
    <div>
        <label>
            Identifier: 
            <InputText @bind-Value="Model!.Id" />
        </label>
    </div>
    <StarshipSubform @bind-Value="Model!.Details" />
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

@code {
    [SupplyParameterFromForm]
    public Ship? Model { get; set; }

    protected override void OnInitialized() => Model ??= new();

    private void Submit()
    {
        Logger.LogInformation("Id = {Id} Desc = {Description} Length = {Length}",
            Model?.Id, Model?.Details?.Description, Model?.Details?.Length);
    }
}

Pokročilé scénáře chyb mapování formulářů

Architektura vytvoří instanci formuláře a naplní FormMappingContext ho, což je kontext přidružený k operaci mapování daného formuláře. Vytvoří instanci každého oboru mapování (definovaného komponentouFormMappingScope).FormMappingContext Pokaždé, když se [SupplyParameterFromForm] zeptá kontextu na hodnotu, architektura naplní FormMappingContext pokusnou hodnotou a všechny chyby mapování.

Vývojáři se neočekává, že budou pracovat FormMappingContext přímo, protože se jedná hlavně o zdroj dat pro InputBase<TValue>, EditContexta další interní implementace, které ukazují chyby mapování jako chyby ověřování. V pokročilých vlastních scénářích můžou vývojáři přistupovat FormMappingContext přímo jako k zápisu vlastního [CascadingParameter] kódu, který využívá pokusy o hodnoty a chyby mapování.

Přepínače

Příklad v této části je založen na Starfleet Starship Database formuláři (Starship3 komponentě) části Příklad formuláře tohoto článku.

Do aplikace přidejte následující enum typy . Vytvořte nový soubor pro jejich uložení nebo je přidejte do Starship.cs souboru.

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

ComponentEnums Zpřístupnění třídy pro:

  • Starship model in Starship.cs (například using static ComponentEnums;).
  • Starfleet Starship Database form () (Starship3.razornapříklad @using static ComponentEnums).

Pomocí InputRadio<TValue> komponent InputRadioGroup<TValue> vytvořte skupinu přepínačů. V následujícím příkladu Starship se vlastnosti přidají do modelu popsaného v části Příklad formuláře článku o vstupních komponentách :

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

Aktualizujte Starfleet Starship Database formulář (Starship3součást) části Příklad formuláře článku Vstupní komponenty. Přidejte komponenty, které se mají vytvořit:

  • Skupina přepínačů pro výrobce lodi.
  • Vnořená skupina přepínačů pro barvu motoru a lodi.

Poznámka:

Vnořené skupiny přepínačů se často nepoužívají ve formulářích, protože můžou vést k neuspořádanému rozložení ovládacích prvků formuláře, které můžou uživatele zmást. Existují však případy, kdy mají smysl v návrhu uživatelského rozhraní, například v následujícím příkladu, který spáruje doporučení pro dva vstupy uživatelů, modul lodí a barvu lodi. Ověření formuláře vyžaduje jeden modul a jednu barvu. Rozložení formuláře používá vnořené InputRadioGroup<TValue>čáry ke spárování motoru a doporučení barev. Uživatel ale může zkombinovat libovolný modul s libovolnou barvou a odeslat formulář.

Poznámka:

Ujistěte se, že ComponentEnums je třída dostupná pro komponentu v následujícím příkladu:

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

Poznámka:

Pokud Name je vynechán, InputRadio<TValue> komponenty jsou seskupené podle jejich nejnovějšího nadřazeného objektu.

Pokud jste implementovali předchozí kód v Starship3 komponentě příklad formuláře oddílu Vstupní komponenty článku, aktualizujte protokolování pro metoduSubmit:Razor

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

Při práci s přepínači ve formuláři se datová vazba zpracovává jinak než jiné prvky, protože přepínače se vyhodnocují jako skupina. Hodnota každého přepínače je pevná, ale hodnota skupiny přepínačů je hodnota vybraného přepínače. Následující příklad ukazuje, jak:

  • Zpracování datové vazby pro skupinu přepínačů
  • Podpora ověřování pomocí vlastní InputRadio<TValue> komponenty

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

Další informace o parametrech obecného typu (@typeparam) najdete v následujících článcích:

Použijte následující ukázkový model.

StarshipModel.cs:

using System.ComponentModel.DataAnnotations;

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

Následující RadioButtonExample komponenta používá předchozí InputRadio komponentu k získání a ověření hodnocení od uživatele:

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