ASP.NET Core Blazor 양식 바인딩

참고 항목

이 문서의 최신 버전은 아닙니다. 현재 릴리스는 이 문서의 .NET 8 버전을 참조 하세요.

Important

이 정보는 상업적으로 출시되기 전에 실질적으로 수정될 수 있는 시험판 제품과 관련이 있습니다. Microsoft는 여기에 제공된 정보에 대해 어떠한 명시적, 또는 묵시적인 보증을 하지 않습니다.

현재 릴리스는 이 문서의 .NET 8 버전을 참조 하세요.

이 문서에서는 양식에서 Blazor 바인딩을 사용하는 방법을 설명합니다.

EditForm/EditContext 모델

형식 EditFormEditContext 다른 구성 요소에 대한 연계 값으로 할당된 개체를 기반으로 합니다. 수정 EditContext 된 양식 필드와 현재 유효성 검사 메시지를 포함하여 편집 프로세스에 대한 메타데이터를 추적합니다. EditForm.Model 또는 EditForm.EditContext을 각각 할당하여 양식을 데이터에 바인딩할 수 있습니다.

모델 바인딩

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

참고 항목

이 문서의 양식 모델 예제 대부분은 C# 속성에 양식을 바인딩하지만 C# 필드 바인딩도 지원됩니다.

컨텍스트 바인딩

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

EditContext또는Model하나EditForm에 할당합니다. 둘 다 할당되면 런타임 오류가 throw됩니다.

지원되는 유형

바인딩은 다음을 지원합니다.

  • 기본 형식
  • 컬렉션
  • 복합 형식
  • 재귀 형식
  • 생성자가 있는 형식
  • 열거형

[IgnoreDataMember] 특성을 사용하여 [DataMember] 모델 바인딩을 사용자 지정할 수도 있습니다. 이러한 특성을 사용하여 속성의 이름을 바꾸고, 속성을 무시하고, 필요에 따라 속성을 표시합니다.

추가 바인딩 옵션

다음을 호출AddRazorComponents할 때 추가 모델 바인딩 옵션을 사용할 수 있습니다RazorComponentsServiceOptions.

다음은 프레임워크에서 할당한 기본값을 보여 줍니다.

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

양식 이름

매개 변수를 FormName 사용하여 양식 이름을 할당합니다. 모델 데이터를 바인딩하려면 양식 이름이 고유해야 합니다. 다음 양식의 이름은 RomulanAle다음과 같습니다.

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

양식 이름 제공:

  • 정적으로 렌더링된 서버 쪽 구성 요소에 의해 제출되는 모든 양식에 필요합니다.
  • 대화형 렌더링 모드가 있는 앱 및 구성 요소의 양식을 포함하는 대화형으로 렌더링된 구성 요소에서 Blazor WebAssembly 제출하는 양식에는 필요하지 않습니다. 그러나 폼에 대해 대화형이 삭제된 경우 런타임 양식 게시 오류를 방지하기 위해 모든 양식에 고유한 양식 이름을 제공하는 것이 좋습니다.

양식 이름은 폼이 정적으로 렌더링된 서버 쪽 구성 요소의 기존 HTTP POST 요청으로 엔드포인트에 게시될 때만 검사. 프레임워크는 폼을 렌더링할 때 예외를 throw하지 않고 HTTP POST가 도착하고 양식 이름을 지정하지 않는 지점에서만 예외를 throw합니다.

기본적으로 앱의 루트 구성 요소 위에 이름 없는(빈 문자열) 양식 범위가 있으며, 앱에 양식 이름 충돌이 없으면 충분합니다. 라이브러리의 양식을 포함하고 라이브러리 개발자가 사용하는 양식 이름을 제어할 수 없는 경우와 같이 양식 이름 충돌이 발생할 수 있는 경우 웹앱의 기본 프로젝트의 구성 요소 Blazor 에 FormMappingScope 양식 이름 범위를 제공합니다.

다음 예제에서 구성 요소에는 HelloFormFromLibrary 이름이 지정된 Hello 양식이 있으며 라이브러리에 있습니다.

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

다음 NamedFormsWithScope 구성 요소는 라이브러리의 HelloFormFromLibrary 구성 요소를 사용하며 이름이 같은 Hello폼도 있습니다. FormMappingScope 구성 요소의 범위 이름은 ParentContext 구성 요소에서 제공하는 모든 양식에 대한 HelloFormFromLibrary 것입니다. 이 예제의 두 폼에는 모두 양식 이름(Hello)이 있지만 양식 이름은 충돌하지 않으며 이벤트는 양식 POST 이벤트에 대해 올바른 양식으로 라우팅됩니다.

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

양식에서 매개 변수 제공([SupplyParameterFromForm])

이 특성은 [SupplyParameterFromForm] 양식의 양식 데이터에서 연결된 속성의 값을 제공해야 임을 나타냅니다. 속성 이름과 일치하는 요청의 데이터는 속성에 바인딩됩니다. 모델 바인딩에 InputBase<TValue> 사용되는 이름과 일치하는 양식 값 이름을 Blazor 기반으로 입력합니다.

특성에 다음 양식 바인딩 매개 변수를 [SupplyParameterFromForm] 지정할 수 있습니다.

  • Name: 매개 변수의 이름을 가져오거나 설정합니다. 이름은 양식 데이터를 일치시키고 값을 바인딩해야 하는지 여부를 결정하는 데 사용할 접두사를 결정하는 데 사용됩니다.
  • FormName: 처리기의 이름을 가져오거나 설정합니다. 이 이름은 매개 변수를 폼 이름으로 일치시켜 값을 바인딩해야 하는지 여부를 결정하는 데 사용됩니다.

다음 예제에서는 양식 이름으로 두 폼을 해당 모델에 독립적으로 바인딩합니다.

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

양식 중첩 및 바인딩

다음 지침에서는 자식 양식을 중첩하고 바인딩하는 방법을 보여 줍니다.

다음 선박 세부 정보 클래스(ShipDetails)는 하위 폼에 대한 설명과 길이를 포함합니다.

ShipDetails.cs:

namespace BlazorSample;

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

다음 Ship 클래스는 식별자(Id)의 이름을 지정하고 선박 세부 정보를 포함합니다.

Ship.cs:

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

다음 하위 폼은 형식의 값을 편집하는 ShipDetails 데 사용됩니다. 이는 구성 요소의 맨 위에 상속하여 Editor<T> 구현됩니다. Editor<T>는 다음 예제ShipDetails에서 자식 구성 요소가 모델()TT을 기반으로 올바른 양식 필드 이름을 생성하도록 합니다.

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>

기본 폼은 클래스에 Ship 바인딩됩니다. 구성 StarshipSubform 요소는 배송 세부 정보를 편집하는 데 사용되며 다음과 같이 바인딩됩니다 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);
    }
}

고급 양식 매핑 오류 시나리오

프레임워크는 지정된 양식의 매핑 작업과 연결된 컨텍스트인 폼에 대한 인스턴스화 및 채우기 FormMappingContext 를 수행합니다. 구성 요소에 의해 정의된 각 매핑 범위는 FormMappingScope 인스턴스화됩니다 FormMappingContext. 컨텍스트에 값을 요청할 때마다 [SupplyParameterFromForm] 프레임워크는 시도된 값과 매핑 오류로 채웁니다 FormMappingContext .

개발자는 매핑 오류를 유효성 검사 오류로 FormMappingContext 표시하기 위해 InputBase<TValue>EditContext기본나 기타 내부 구현의 데이터 원본이기 때문에 직접 상호 작용할 필요가 없습니다. 고급 사용자 지정 시나리오에서 개발자는 시도된 값과 매핑 오류를 사용하는 사용자 지정 코드를 작성하기 위해 직접 [CascadingParameter] 액세스할 FormMappingContext 수 있습니다.

라디오 단추

이 섹션의 예제는 이 문서의 예제 양식 섹션의 (Starship3구성 요소)을 기반으로 Starfleet Starship Database 합니다.

앱에 다음 enum 유형을 추가합니다. 보관할 새 파일을 생성하거나 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 }
}

다음에서 클래스에 enums 액세스할 수 있도록 합니다.

  • Starship 모델( Starship.cs 예: using static ComponentEnums;).
  • Starfleet Starship Database form(Starship3.razor)(예: @using static ComponentEnums)입니다.

InputRadioGroup<TValue> 구성 요소와 함께 InputRadio<TValue> 구성 요소를 사용하여 라디오 단추 그룹을 만듭니다. 다음 예제에서는 입력 구성 요소 문서의 예제 양식 섹션에 설명된 모델에 속성이 추가 Starship 됩니다.

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

Starfleet Starship Database 입력 구성 요소 문서의 예제 양식 섹션에 있는 (Starship3구성 요소)을 업데이트합니다. 생성할 구성 요소를 추가합니다.

  • 선박 제조업체의 라디오 단추 그룹.
  • Ship 색상 및 엔진에 해당하는 중첩된 라디오 단추 그룹.

참고 항목

중첩된 라디오 단추 그룹은 사용자에게 혼란을 줄 수 있는 양식 컨트롤의 구성되지 않은 레이아웃을 만들 수 있기 때문에 양식에서 자주 사용되지 않습니다. 그러나 두 사용자의 입력과 Ship 엔진 및 Ship 색상에 대한 권장 사항을 결합하는 다음 예제와 같이 UI 디자인 측면에서 의미 있는 경우가 있습니다. 양식의 유효성 검사에는 하나의 엔진과 하나의 색상이 필요합니다. 양식의 레이아웃은 중첩된 InputRadioGroup<TValue>을 사용하여 엔진 및 색상 권장 사항을 쌍으로 만듭니다. 그러나 사용자는 모든 엔진과 모든 색을 결합하여 양식을 제출할 수 있습니다.

참고 항목

다음 예제에서는 구성 요소에서 ComponentEnums 클래스를 사용할 수 있도록 해야 합니다.

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

참고 항목

Name이 생략되면 InputRadio<TValue> 구성 요소가 가장 최근 상위 항목을 기준으로 그룹화됩니다.

입력 구성 요소 문서의 예제 양식 섹션 구성 요소에서 Starship3 이전 Razor 태그를 구현한 경우 메서드에 대한 로깅을 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);

양식에서 라디오 단추를 사용하는 경우, 라디오 단추는 그룹으로 평가되기 때문에 데이터 바인딩이 다른 요소와 다르게 처리됩니다. 각 라디오 단추의 값은 고정되어 있지만 라디오 단추 그룹의 값은 선택한 라디오 단추의 값입니다. 아래 예제는 다음과 같은 작업의 방법을 보여 줍니다.

  • 라디오 단추 그룹의 데이터 바인딩을 처리합니다.
  • 사용자 지정 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;
        }
    }
}

제네릭 형식 매개 변수(@typeparam)에 대한 자세한 내용은 다음 문서를 참조하세요.

다음 예제 모델을 사용합니다.

StarshipModel.cs:

using System.ComponentModel.DataAnnotations;

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

다음 RadioButtonExample 구성 요소는 이전 InputRadio 구성 요소를 사용하여 사용자의 평가를 가져오고 유효성을 검사합니다.

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