ASP.NET Core Blazor powiązania danychASP.NET Core Blazor data binding

Luke Latham, Daniel Rothi Steve SandersonBy Luke Latham, Daniel Roth, and Steve Sanderson

Razor składniki zapewniają funkcje powiązań danych za pośrednictwem atrybutu elementu HTML o nazwie @bind z wartością pola, właściwości lub Razor wyrażenia.Razor components provide data binding features via an HTML element attribute named @bind with a field, property, or Razor expression value.

Poniższy przykład wiąże <input> element z currentValue polem i <input> elementem CurrentValue Właściwości:The following example binds an <input> element to the currentValue field and an <input> element to the CurrentValue property:

<p>
    <input @bind="currentValue" /> Current value: @currentValue
</p>

<p>
    <input @bind="CurrentValue" /> Current value: @CurrentValue
</p>

@code {
    private string currentValue;

    private string CurrentValue { get; set; }
}

Gdy jeden z elementów utraci fokus, jego powiązane pole lub właściwość jest aktualizowana.When one of the elements loses focus, its bound field or property is updated.

Pole tekstowe jest aktualizowane w interfejsie użytkownika tylko wtedy, gdy składnik jest renderowany, a nie w odpowiedzi na zmianę wartości pola lub właściwości.The text box is updated in the UI only when the component is rendered, not in response to changing the field's or property's value. Ponieważ składniki renderują się po wykonaniu kodu procedury obsługi zdarzeń, aktualizacje pól i właściwości są zwykle odzwierciedlane w interfejsie użytkownika natychmiast po wyzwoleniu programu obsługi zdarzeń.Since components render themselves after event handler code executes, field and property updates are usually reflected in the UI immediately after an event handler is triggered.

Używanie @bind z CurrentValue właściwością ( <input @bind="CurrentValue" /> ) jest zasadniczo równoważne z następującymi:Using @bind with the CurrentValue property (<input @bind="CurrentValue" />) is essentially equivalent to the following:

<input value="@CurrentValue"
    @onchange="@((ChangeEventArgs __e) => CurrentValue = 
        __e.Value.ToString())" />

@code {
    private string CurrentValue { get; set; }
}

Gdy składnik jest renderowany, value element wejściowy pochodzi z CurrentValue właściwości.When the component is rendered, the value of the input element comes from the CurrentValue property. Gdy użytkownik wpisze w polu tekstowym i zmieni fokus elementu, onchange zdarzenie jest wywoływane i CurrentValue Właściwość jest ustawiana na wartość zmieniona.When the user types in the text box and changes element focus, the onchange event is fired and the CurrentValue property is set to the changed value. W rzeczywistości generowanie kodu jest bardziej złożone niż to, ponieważ @bind obsługuje przypadki, w których są wykonywane konwersje typów.In reality, the code generation is more complex than that because @bind handles cases where type conversions are performed. W zasadzie @bind kojarzy bieżącą wartość wyrażenia z value atrybutem i obsługuje zmiany przy użyciu zarejestrowanej procedury obsługi.In principle, @bind associates the current value of an expression with a value attribute and handles changes using the registered handler.

Powiąż właściwość lub pole w innych zdarzeniach, dołączając także @bind:event atrybut z event parametrem.Bind a property or field on other events by also including an @bind:event attribute with an event parameter. Poniższy przykład wiąże CurrentValue Właściwość oninput zdarzenia:The following example binds the CurrentValue property on the oninput event:

<input @bind="CurrentValue" @bind:event="oninput" />

@code {
    private string CurrentValue { get; set; }
}

W przeciwieństwie do onchange , które jest wyzwalane, gdy element utraci fokus, oninput jest uruchamiany po zmianie wartości pola tekstowego.Unlike onchange, which fires when the element loses focus, oninput fires when the value of the text box changes.

W powiązaniu atrybutu rozróżniana jest wielkość liter:Attribute binding is case sensitive:

  • @bind jest prawidłowy.@bind is valid.
  • @Bind i @BIND są nieprawidłowe.@Bind and @BIND are invalid.

Wartości niemożliwy do przeanalizowaniaUnparsable values

Gdy użytkownik dostarczy wartość niemożliwy do przeanalizowania do elementu powiązanego z danymi, wartość niemożliwy do przeanalizowania jest automatycznie przywracana do poprzedniej wartości po wyzwoleniu zdarzenia bind.When a user provides an unparsable value to a databound element, the unparsable value is automatically reverted to its previous value when the bind event is triggered.

Poniżej przedstawiono przykładowy scenariusz:Consider the following scenario:

  • <input>Element jest powiązany z int typem z początkową wartością 123 :An <input> element is bound to an int type with an initial value of 123:

    <input @bind="inputValue" />
    
    @code {
        private int inputValue = 123;
    }
    
  • Użytkownik aktualizuje wartość elementu na 123.45 liście na stronie i zmienia fokus elementu.The user updates the value of the element to 123.45 in the page and changes the element focus.

W poprzednim scenariuszu wartość elementu jest przywracana 123 .In the preceding scenario, the element's value is reverted to 123. Gdy wartość 123.45 zostanie odrzucona na korzyść oryginalnej wartości 123 , użytkownik rozumie, że ich wartość nie została zaakceptowana.When the value 123.45 is rejected in favor of the original value of 123, the user understands that their value wasn't accepted.

Domyślnie powiązanie dotyczy onchange zdarzenia elementu ( @bind="{PROPERTY OR FIELD}" ).By default, binding applies to the element's onchange event (@bind="{PROPERTY OR FIELD}"). Służy @bind="{PROPERTY OR FIELD}" @bind:event={EVENT} do wyzwalania powiązań na innym zdarzeniu.Use @bind="{PROPERTY OR FIELD}" @bind:event={EVENT} to trigger binding on a different event. W przypadku oninput zdarzenia ( @bind:event="oninput" ) jego wersja następuje po naciśnięciu klawisza, które wprowadza niemożliwy do przeanalizowania wartość.For the oninput event (@bind:event="oninput"), the reversion occurs after any keystroke that introduces an unparsable value. Podczas określania wartości docelowej dla oninput zdarzenia z int typem związanym użytkownik nie jest w trakcie wpisywania . znaku.When targeting the oninput event with an int-bound type, a user is prevented from typing a . character. .Znak zostanie natychmiast usunięty, więc użytkownik otrzymuje natychmiastową opinię, że dozwolone są tylko liczby całkowite.A . character is immediately removed, so the user receives immediate feedback that only whole numbers are permitted. Istnieją scenariusze, w których przywrócenie wartości oninput zdarzenia nie jest idealne, na przykład wtedy, gdy użytkownik powinien mieć możliwość wyczyszczenia wartości, która nie może być przewidziana <input> .There are scenarios where reverting the value on the oninput event isn't ideal, such as when the user should be allowed to clear an unparsable <input> value. Alternatywy obejmują:Alternatives include:

  • Nie używaj oninput zdarzenia.Don't use the oninput event. Użyj zdarzenia domyślnego onchange (tylko określenie @bind="{PROPERTY OR FIELD}" ), gdzie nie jest przywracana nieprawidłowa wartość, dopóki element nie utraci fokusu.Use the default onchange event (only specify @bind="{PROPERTY OR FIELD}"), where an invalid value isn't reverted until the element loses focus.
  • Powiąż z typem dopuszczającym wartość null, na przykład int? lub string i podaj logikę niestandardową do obsługi nieprawidłowych wpisów.Bind to a nullable type, such as int? or string and provide custom logic to handle invalid entries.
  • Użyj składnika walidacji formularza, takiego jak InputNumber<TValue> lub InputDate<TValue> .Use a form validation component, such as InputNumber<TValue> or InputDate<TValue>. Składniki walidacji formularza mają wbudowaną obsługę zarządzania nieprawidłowymi danymi wejściowymi.Form validation components have built-in support to manage invalid inputs. Aby uzyskać więcej informacji, zobacz ASP.NET Core Blazor formularzy i walidacji.For more information, see ASP.NET Core Blazor formularzy i walidacji. Składniki walidacji formularza:Form validation components:
    • Zezwalaj użytkownikowi na dostarczenie nieprawidłowych danych wejściowych i odbieranie błędów walidacji w skojarzonym EditContext .Permit the user to provide invalid input and receive validation errors on the associated EditContext.
    • Wyświetlaj błędy walidacji w interfejsie użytkownika bez zakłócania wprowadzania dodatkowych danych przez użytkownika.Display validation errors in the UI without interfering with the user entering additional webform data.

Ciągi formatująceFormat strings

Powiązanie danych działa z DateTime ciągami formatu przy użyciu @bind:format .Data binding works with DateTime format strings using @bind:format. W tej chwili nie są dostępne inne wyrażenia formatu, takie jak formaty walutowe lub liczbowe.Other format expressions, such as currency or number formats, aren't available at this time.

<input @bind="startDate" @bind:format="yyyy-MM-dd" />

@code {
    private DateTime startDate = new DateTime(2020, 1, 1);
}

W poprzednim kodzie <input> Typ pola () elementu jest type wartością domyślną text .In the preceding code, the <input> element's field type (type) defaults to text. @bind:format jest obsługiwana w celu powiązania następujących typów .NET:@bind:format is supported for binding the following .NET types:

Ten @bind:format atrybut określa format daty, który ma zostać zastosowany do value <input> elementu.The @bind:format attribute specifies the date format to apply to the value of the <input> element. Format jest również używany do analizowania wartości w przypadku onchange wystąpienia zdarzenia.The format is also used to parse the value when an onchange event occurs.

Określanie formatu dla date typu pola nie jest zalecane, ponieważ Blazor ma wbudowaną obsługę formatowania dat.Specifying a format for the date field type isn't recommended because Blazor has built-in support to format dates. Pomimo zalecenia, należy używać tylko yyyy-MM-dd formatu daty dla powiązania, aby prawidłowo działać, jeśli format jest dostarczany z date typem pola:In spite of the recommendation, only use the yyyy-MM-dd date format for binding to function correctly if a format is supplied with the date field type:

<input type="date" @bind="startDate" @bind:format="yyyy-MM-dd">

Powiązanie z parametrami składnikaBinding with component parameters

Typowy scenariusz polega na powiązaniu właściwości w składniku podrzędnym z właściwością w jej elemencie nadrzędnym.A common scenario is binding a property in a child component to a property in its parent. Ten scenariusz jest nazywany powiązaniem łańcuchowym , ponieważ wiele poziomów powiązań występuje jednocześnie.This scenario is called a chained bind because multiple levels of binding occur simultaneously.

Parametry składnika umożliwiają powiązanie właściwości i pól składnika nadrzędnego z @bind-{PROPERTY OR FIELD} składnią.Component parameters permit binding properties and fields of a parent component with @bind-{PROPERTY OR FIELD} syntax.

Nie można zaimplementować powiązań łańcuchowych przy użyciu @bind składni w składniku podrzędnym.Chained binds can't be implemented with @bind syntax in the child component. Procedura obsługi zdarzeń i wartość musi być określona oddzielnie, aby można było obsługiwać aktualizowanie właściwości w elemencie nadrzędnym ze składnika podrzędnego.An event handler and value must be specified separately to support updating the property in the parent from the child component.

Składnik nadrzędny nadal wykorzystuje @bind składnię w celu skonfigurowania powiązania danych ze składnikiem podrzędnym.The parent component still leverages the @bind syntax to set up the data-binding with the child component.

Następujący Child składnik ( Shared/Child.razor ) ma Year parametr składnika i YearChanged wywołanie zwrotne:The following Child component (Shared/Child.razor) has a Year component parameter and YearChanged callback:

<div class="card bg-light mt-3" style="width:18rem ">
    <div class="card-body">
        <h3 class="card-title">Child Component</h3>
        <p class="card-text">Child <code>Year</code>: @Year</p>
    </div>
</div>

<button @onclick="UpdateYearFromChild">Update Year from Child</button>

@code {
    private Random r = new Random();

    [Parameter]
    public int Year { get; set; }

    [Parameter]
    public EventCallback<int> YearChanged { get; set; }

    private async Task UpdateYearFromChild()
    {
        await YearChanged.InvokeAsync(r.Next(1950, 2021));
    }
}

Wywołanie zwrotne ( EventCallback<TValue> ) musi być nazwane jako nazwa parametru składnika, po którym następuje Changed sufiks "" {PARAMETER NAME}Changed .The callback (EventCallback<TValue>) must be named as the component parameter name followed by the "Changed" suffix ({PARAMETER NAME}Changed). W poprzednim przykładzie wywołanie zwrotne ma nazwę YearChanged .In the preceding example, the callback is named YearChanged. EventCallback.InvokeAsync Wywołuje delegata skojarzonego z powiązaniem z podanym argumentem i wysyła powiadomienie o zdarzeniu dla zmienionej właściwości.EventCallback.InvokeAsync invokes the delegate associated with the binding with the provided argument and dispatches an event notification for the changed property.

W poniższym Parent składniku ( Parent.razor ) year pole jest powiązane z Year parametrem składnika podrzędnego:In the following Parent component (Parent.razor), the year field is bound to the Year parameter of the child component:

@page "/Parent"

<h1>Parent Component</h1>

<p>Parent <code>year</code>: @year</p>

<button @onclick="UpdateYear">Update Parent <code>year</code></button>

<Child @bind-Year="year" />

@code {
    private Random r = new Random();
    private int year = 1979;

    private void UpdateYear()
    {
        year = r.Next(1950, 2021);
    }
}

YearParametr jest możliwy do powiązania, ponieważ ma zdarzenie towarzyszące YearChanged pasujące do typu Year parametru.The Year parameter is bindable because it has a companion YearChanged event that matches the type of the Year parameter.

Zgodnie z Konwencją Właściwość można powiązać z odpowiadającą jej obsługą zdarzeń, dołączając @bind-{PROPERTY}:event atrybut przypisany do procedury obsługi.By convention, a property can be bound to a corresponding event handler by including an @bind-{PROPERTY}:event attribute assigned to the handler. <Child @bind-Year="year" /> jest odpowiednikiem zapisu:<Child @bind-Year="year" /> is equivalent to writing:

<Child @bind-Year="year" @bind-Year:event="YearChanged" />

W bardziej zaawansowanym i rzeczywistym przykładzie Poniższy PasswordField składnik ( PasswordField.razor ):In a more sophisticated and real-world example, the following PasswordField component (PasswordField.razor):

  • Ustawia <input> wartość elementu na password pole.Sets an <input> element's value to a password field.
  • Uwidacznia zmiany właściwości w Password składniku nadrzędnym EventCallback , który przekazuje w bieżącej wartości pola elementu podrzędnego password jako argument.Exposes changes of a Password property to a parent component with an EventCallback that passes in the current value of the child's password field as its argument.
  • Używa onclick zdarzenia do wyzwolenia ToggleShowPassword metody.Uses the onclick event to trigger the ToggleShowPassword method. Aby uzyskać więcej informacji, zobacz BlazorObsługa zdarzeń ASP.NET Core.For more information, see BlazorObsługa zdarzeń ASP.NET Core.
<h1>Provide your password</h1>

Password:

<input @oninput="OnPasswordChanged" 
       required 
       type="@(showPassword ? "text" : "password")" 
       value="@password" />

<button class="btn btn-primary" @onclick="ToggleShowPassword">
    Show password
</button>

@code {
    private bool showPassword;
    private string password;

    [Parameter]
    public string Password { get; set; }

    [Parameter]
    public EventCallback<string> PasswordChanged { get; set; }

    private async Task OnPasswordChanged(ChangeEventArgs e)
    {
        password = e.Value.ToString();

        await PasswordChanged.InvokeAsync(password);
    }

    private void ToggleShowPassword()
    {
        showPassword = !showPassword;
    }
}

PasswordFieldSkładnik jest używany w innym składniku:The PasswordField component is used in another component:

@page "/Parent"

<h1>Parent Component</h1>

<PasswordField @bind-Password="password" />

@code {
    private string password;
}

Wykonaj sprawdzenia lub błędy pułapki w metodzie, która wywołuje delegata powiązania.Perform checks or trap errors in the method that invokes the binding's delegate. Poniższy przykład przedstawia natychmiastową opinię dla użytkownika, jeśli w wartości hasła jest używana spacja:The following example provides immediate feedback to the user if a space is used in the password's value:

<h1>Child Component</h1>

Password: 

<input @oninput="OnPasswordChanged" 
       required 
       type="@(showPassword ? "text" : "password")" 
       value="@password" />

<button class="btn btn-primary" @onclick="ToggleShowPassword">
    Show password
</button>

<span class="text-danger">@validationMessage</span>

@code {
    private bool showPassword;
    private string password;
    private string validationMessage;

    [Parameter]
    public string Password { get; set; }

    [Parameter]
    public EventCallback<string> PasswordChanged { get; set; }

    private Task OnPasswordChanged(ChangeEventArgs e)
    {
        password = e.Value.ToString();

        if (password.Contains(' '))
        {
            validationMessage = "Spaces not allowed!";

            return Task.CompletedTask;
        }
        else
        {
            validationMessage = string.Empty;

            return PasswordChanged.InvokeAsync(password);
        }
    }

    private void ToggleShowPassword()
    {
        showPassword = !showPassword;
    }
}

Aby uzyskać więcej informacji na temat EventCallback<TValue> , zobacz BlazorObsługa zdarzeń ASP.NET Core .For more information on EventCallback<TValue>, see BlazorObsługa zdarzeń ASP.NET Core.

Powiązywanie w więcej niż dwóch składnikachBind across more than two components

Można powiązać dowolną liczbę składników zagnieżdżonych, ale należy przestrzegać jednokierunkowego przepływu danych:You can bind through any number of nested components, but you must respect the one-way flow of data:

  • Powiadomienia o zmianach przepływają w górę hierarchii.Change notifications flow up the hierarchy.
  • Nowe wartości parametrów przepływają w dół hierarchii.New parameter values flow down the hierarchy.

Typowym i Zalecanym podejściem jest przechowywanie wyłącznie danych źródłowych w składniku nadrzędnym, aby uniknąć wszelkich pomyłek dotyczących stanu, który należy zaktualizować.A common and recommended approach is to only store the underlying data in the parent component to avoid any confusion about what state must be updated.

Poniższe składniki przedstawiają powyższe koncepcje:The following components demonstrate the preceding concepts:

ParentComponent.razor:ParentComponent.razor:

<h1>Parent Component</h1>

<p>Parent Message: <b>@parentMessage</b></p>

<p>
    <button @onclick="ChangeValue">Change from Parent</button>
</p>

<ChildComponent @bind-ChildMessage="parentMessage" />

@code {
    private string parentMessage = "Initial value set in Parent";

    private void ChangeValue()
    {
        parentMessage = $"Set in Parent {DateTime.Now}";
    }
}

ChildComponent.razor:ChildComponent.razor:

<div class="border rounded m-1 p-1">
    <h2>Child Component</h2>

    <p>Child Message: <b>@ChildMessage</b></p>

    <p>
        <button @onclick="ChangeValue">Change from Child</button>
    </p>

    <GrandchildComponent @bind-GrandchildMessage="BoundValue" />
</div>

@code {
    [Parameter]
    public string ChildMessage { get; set; }

    [Parameter]
    public EventCallback<string> ChildMessageChanged { get; set; }

    private string BoundValue
    {
        get => ChildMessage;
        set => ChildMessageChanged.InvokeAsync(value);
    }

    private async Task ChangeValue()
    {
        await ChildMessageChanged.InvokeAsync(
            $"Set in Child {DateTime.Now}");
    }
}

GrandchildComponent.razor:GrandchildComponent.razor:

<div class="border rounded m-1 p-1">
    <h3>Grandchild Component</h3>

    <p>Grandchild Message: <b>@GrandchildMessage</b></p>

    <p>
        <button @onclick="ChangeValue">Change from Grandchild</button>
    </p>
</div>

@code {
    [Parameter]
    public string GrandchildMessage { get; set; }

    [Parameter]
    public EventCallback<string> GrandchildMessageChanged { get; set; }

    private async Task ChangeValue()
    {
        await GrandchildMessageChanged.InvokeAsync(
            $"Set in Grandchild {DateTime.Now}");
    }
}

W przypadku alternatywnego podejścia przystosowanego do udostępniania danych w pamięci między składnikami, które nie są niekoniecznie zagnieżdżone, zobacz BlazorZarządzanie stanem ASP.NET Core .For an alternative approach suited to sharing data in-memory across components that aren't necessarily nested, see BlazorZarządzanie stanem ASP.NET Core.

Dodatkowe zasobyAdditional resources