ASP.NET Core Blazor-DatenbindungASP.NET Core Blazor data binding

Von Luke Latham, Daniel Roth und Steve SandersonBy Luke Latham, Daniel Roth, and Steve Sanderson

Razor-Komponenten stellen Features zur Datenbindung über ein HTML-Elementattribut namens @bind bereit, mit einem Feld, einer Eigenschaft oder einem Razor-Ausdruckswert.Razor components provide data binding features via an HTML element attribute named @bind with a field, property, or Razor expression value.

Im folgenden Beispiel wird ein <input>-Element an das currentValue-Feld und ein <input>-Element an die CurrentValue-Eigenschaft gebunden: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; }
}

Wenn eines der Elemente den Fokus verliert, wird das gebundene Feld oder die Eigenschaft aktualisiert.When one of the elements loses focus, its bound field or property is updated.

Das Textfeld wird in der Benutzeroberfläche nur dann aktualisiert, wenn die Komponente gerendert wird, nicht als Reaktion auf die Änderung des Werts des Felds oder der Eigenschaft.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. Da sich Komponenten nach der Ausführung von Ereignishandlercode selbst rendern, werden Feld- und Eigenschaftsaktualisierungen in der Regel unmittelbar nach dem Auslösen eines Ereignishandlers in der Benutzeroberfläche widergespiegelt.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.

Die Verwendung von @bind mit der CurrentValue-Eigenschaft (<input @bind="CurrentValue" />) entspricht im Wesentlichen dem Folgenden: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; }
}

Wenn die Komponente gerendert wird, stammt der value des Eingabeelements aus der CurrentValue-Eigenschaft.When the component is rendered, the value of the input element comes from the CurrentValue property. Wenn der Benutzer in das Textfeld eingibt und den Elementfokus ändert, wird das Ereignis onchange ausgelöst und die Eigenschaft CurrentValue auf den geänderten Wert festgelegt.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. In Wirklichkeit ist die Codegenerierung komplexer, weil @bind Fälle behandelt, in denen Typkonvertierungen durchgeführt werden.In reality, the code generation is more complex than that because @bind handles cases where type conversions are performed. Im Prinzip assoziiert @bind den aktuellen Wert eines Ausdrucks mit einem value-Attribut und behandelt Änderungen mit dem registrierten Handler.In principle, @bind associates the current value of an expression with a value attribute and handles changes using the registered handler.

Binden Sie eine Eigenschaft oder ein Feld an andere Ereignisse, indem Sie auch ein @bind:event-Attribut mit einem event-Parameter einbeziehen.Bind a property or field on other events by also including an @bind:event attribute with an event parameter. Das folgende Beispiel bindet die Eigenschaft CurrentValue an das Ereignis oninput:The following example binds the CurrentValue property on the oninput event:

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

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

Im Gegensatz zu onchange, das ausgelöst wird, wenn das Element den Fokus verliert, wird oninput ausgelöst, wenn sich der Wert des Textfelds ändert.Unlike onchange, which fires when the element loses focus, oninput fires when the value of the text box changes.

Bei der Attributbindung wird die Groß- und Kleinschreibung berücksichtigt.Attribute binding is case sensitive:

  • @bind ist gültig.@bind is valid.
  • @Bind und @BIND sind ungültig.@Bind and @BIND are invalid.

Nicht analysierbare WerteUnparsable values

Wenn ein Benutzer einem Element mit Datenbindung einen nicht analysierbaren Wert zur Verfügung stellt, wird der nicht analysierbare Wert automatisch auf seinen vorherigen Wert zurückgesetzt, wenn das Bindungsereignis ausgelöst wird.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.

Betrachten Sie das folgende Szenario:Consider the following scenario:

  • Ein <input>-Element ist an einen int-Typ mit einem Anfangswert von 123 gebunden:An <input> element is bound to an int type with an initial value of 123:

    <input @bind="inputValue" />
    
    @code {
        private int inputValue = 123;
    }
    
  • Der Benutzer aktualisiert den Wert des Elements auf der Seite zu 123.45 und ändert den Elementfokus.The user updates the value of the element to 123.45 in the page and changes the element focus.

Im vorangehenden Szenario wird der Wert des Elements auf 123 zurückgesetzt.In the preceding scenario, the element's value is reverted to 123. Wenn der Wert 123.45 zugunsten des ursprünglichen Werts von 123 abgelehnt wird, versteht der Benutzer, dass sein Wert nicht akzeptiert wurde.When the value 123.45 is rejected in favor of the original value of 123, the user understands that their value wasn't accepted.

Standardmäßig gilt die Bindung für das onchange-Ereignis des Elements (@bind="{PROPERTY OR FIELD}").By default, binding applies to the element's onchange event (@bind="{PROPERTY OR FIELD}"). Verwenden Sie @bind="{PROPERTY OR FIELD}" @bind:event={EVENT}, um die Bindung bei einem anderen Ereignis auszulösen.Use @bind="{PROPERTY OR FIELD}" @bind:event={EVENT} to trigger binding on a different event. Für das oninput-Ereignis (@bind:event="oninput") erfolgt die Umkehrung nach jedem Tastendruck, der einen nicht analysierbaren Wert einführt.For the oninput event (@bind:event="oninput"), the reversion occurs after any keystroke that introduces an unparsable value. Wenn das oninput-Ereignis mit einem int-gebundenen Typ angestrebt wird, wird ein Benutzer daran gehindert, ein .-Zeichen einzugeben.When targeting the oninput event with an int-bound type, a user is prevented from typing a . character. Ein .-Zeichen wird sofort entfernt, sodass der Benutzer sofort die Rückmeldung erhält, dass nur ganze Zahlen zulässig sind.A . character is immediately removed, so the user receives immediate feedback that only whole numbers are permitted. Es gibt Szenarien, in denen die Umkehrung des Werts auf das oninput Ereignis nicht ideal ist, z. B. wenn dem Benutzer erlaubt werden soll, einen nicht analysierbaren <input>-Wert zu löschen.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. Zu den Alternativen gehören:Alternatives include:

  • Verwenden Sie nicht das Ereignis oninput.Don't use the oninput event. Verwenden Sie das Standardereignis onchange (geben Sie nur @bind="{PROPERTY OR FIELD}" an), bei dem ein ungültiger Wert erst dann rückgängig gemacht wird, wenn das Element den Fokus verliert.Use the default onchange event (only specify @bind="{PROPERTY OR FIELD}"), where an invalid value isn't reverted until the element loses focus.
  • Binden Sie an einen Nullable-Typ, z. B. int? oder string, und stellen Sie benutzerdefinierte Logik zur Behandlung ungültiger Einträge bereit.Bind to a nullable type, such as int? or string and provide custom logic to handle invalid entries.
  • Verwenden Sie eine Formularüberprüfungskomponente wie InputNumber<TValue> oder InputDate<TValue>.Use a form validation component, such as InputNumber<TValue> or InputDate<TValue>. Formularüberprüfungskomponenten verfügen über die integrierte Unterstützung der Verwaltung ungültiger Eingaben.Form validation components have built-in support to manage invalid inputs. Weitere Informationen finden Sie unter Blazor-Formulare und -Validierung in ASP.NET Core.For more information, see Blazor-Formulare und -Validierung in ASP.NET Core. Formularüberprüfungskomponenten:Form validation components:
    • Erlauben Sie dem Benutzer, ungültige Eingaben zu machen und Überprüfungsfehler für das zugehörige EditContext zu erhalten.Permit the user to provide invalid input and receive validation errors on the associated EditContext.
    • Zeigen Sie Überprüfungsfehler auf der Benutzeroberfläche an, ohne den Benutzer bei der Eingabe zusätzlicher WebForm-Daten zu beeinträchtigen.Display validation errors in the UI without interfering with the user entering additional webform data.

FormatzeichenfolgenFormat strings

Die Datenbindung funktioniert mit DateTime-Formatzeichenfolgen, die @bind:format verwenden.Data binding works with DateTime format strings using @bind:format. Andere Formatausdrücke wie Währungs- oder Zahlenformate sind zu diesem Zeitpunkt nicht verfügbar.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);
}

Im vorhergehenden Code ist der Feldtyp des <input>-Elements (type) standardmäßig auf text festgelegt.In the preceding code, the <input> element's field type (type) defaults to text. @bind:format wird für die Bindung der folgenden .NET-Typen unterstützt:@bind:format is supported for binding the following .NET types:

Das @bind:format-Attribut gibt das Datumsformat an, das auf den value des <input>-Elements angewendet werden soll.The @bind:format attribute specifies the date format to apply to the value of the <input> element. Das Format wird auch verwendet, um den Wert beim Eintreten eines onchange-Ereignisses zu analysieren.The format is also used to parse the value when an onchange event occurs.

Die Angabe eines Formats für den Feldtyp date wird nicht empfohlen, da Blazor eine integrierte Unterstützung für die Formatierung von Daten bietet.Specifying a format for the date field type isn't recommended because Blazor has built-in support to format dates. Verwenden Sie trotz der Empfehlung nur dann das Datumsformat yyyy-MM-dd für die Bindung, um ordnungsgemäß zu funktionieren, wenn ein Format mit dem Feldtyp date bereitgestellt wird: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">

Binden mit KomponentenparameternBinding with component parameters

Ein häufiges Szenario besteht darin, eine Eigenschaft in einer untergeordneten Komponente an eine Eigenschaft in ihrem übergeordneten Element zu binden.A common scenario is binding a property in a child component to a property in its parent. Dieses Szenario wird als verkettete Bindung bezeichnet, da mehrere Ebenen der Bindung gleichzeitig auftreten.This scenario is called a chained bind because multiple levels of binding occur simultaneously.

Komponentenparameter erlauben das Binden von Eigenschaften und Feldern einer übergeordneten Komponente mit @bind-{PROPERTY OR FIELD}-Syntax.Component parameters permit binding properties and fields of a parent component with @bind-{PROPERTY OR FIELD} syntax.

Verkettete Bindungen können nicht mit der @bind-Syntax in der untergeordneten Komponente implementiert werden.Chained binds can't be implemented with @bind syntax in the child component. Separat müssen ein Ereignishandler und ein Wert angegeben werden, um die Aktualisierung der Eigenschaft in der übergeordneten Komponente zu unterstützen.An event handler and value must be specified separately to support updating the property in the parent from the child component.

Die übergeordnete Komponente nutzt weiterhin die @bind-Syntax, um die Datenbindung mit der untergeordneten Komponente einzurichten.The parent component still leverages the @bind syntax to set up the data-binding with the child component.

Die folgende Child-Komponente (Shared/Child.razor) verfügt über einen Year-Komponentenparameter und YearChanged-Rückruf: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));
    }
}

Der Rückruf (EventCallback<TValue>) muss den Namen des Komponentenparameters aufweisen, gefolgt vom Suffix „Changed“ ({PARAMETER NAME}Changed).The callback (EventCallback<TValue>) must be named as the component parameter name followed by the "Changed" suffix ({PARAMETER NAME}Changed). Im vorherigen Beispiel trägt der Rückruf den Namen YearChanged.In the preceding example, the callback is named YearChanged. EventCallback.InvokeAsync ruft den Delegaten, der der Bindung zugeordnet ist, mit dem bereitgestellten Argument auf und sendet eine Ereignisbenachrichtigung über die geänderte Eigenschaft.EventCallback.InvokeAsync invokes the delegate associated with the binding with the provided argument and dispatches an event notification for the changed property.

In der folgenden Parent-Komponente (Parent.razor) ist das Feld year an den Year-Parameter der untergeordneten Komponente gebunden: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);
    }
}

Der Parameter Year ist bindbar, da er ein Begleitereignis YearChanged aufweist, das dem Typ des Parameters Year entspricht.The Year parameter is bindable because it has a companion YearChanged event that matches the type of the Year parameter.

Gemäß der Konvention kann eine Eigenschaft an einen entsprechenden Ereignishandler gebunden werden, indem ein @bind-{PROPERTY}:event-Attribut einbezogen wird, das dem Handler zugewiesen ist.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" /> ist identisch mit dem folgenden Code:<Child @bind-Year="year" /> is equivalent to writing:

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

In einem komplexeren Beispiel aus der Praxis wird die PasswordField-Komponente (PasswordField.razor) für Folgendes verwendet:In a more sophisticated and real-world example, the following PasswordField component (PasswordField.razor):

  • Legt den Wert eines <input>-Elements auf ein password-Feld fest.Sets an <input> element's value to a password field.
  • Macht Änderungen einer Password-Eigenschaft einer übergeordneten Komponente mit einem EventCallback verfügbar, der den aktuellen Wert des password-Felds des untergeordneten Elements als Argument übergibt.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.
  • Verwendet das onclick-Ereignis zum Auslösen der ToggleShowPassword-Methode.Uses the onclick event to trigger the ToggleShowPassword method. Weitere Informationen finden Sie unter ASP.NET Core Blazor-Ereignisbehandlung.For more information, see ASP.NET Core Blazor-Ereignisbehandlung.
<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;
    }
}

Die PasswordField-Komponente wird in einer anderen Komponente verwendet:The PasswordField component is used in another component:

@page "/Parent"

<h1>Parent Component</h1>

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

@code {
    private string password;
}

Führen Sie Prüfungen durch, oder fangen Sie Fehler in der Methode ab, die den Delegat der Bindung aufruft.Perform checks or trap errors in the method that invokes the binding's delegate. Das folgende Beispiel gibt dem Benutzer ein sofortiges Feedback, wenn ein Leerzeichen im Wert des Kennworts verwendet wird: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;
    }
}

Weitere Informationen zu EventCallback<TValue> finden Sie unter ASP.NET Core Blazor-Ereignisbehandlung.For more information on EventCallback<TValue>, see ASP.NET Core Blazor-Ereignisbehandlung.

Binden über mehr als zwei KomponentenBind across more than two components

Sie können eine Bindung über eine beliebige Anzahl geschachtelter Komponenten erstellen, Sie müssen aber den unidirektionalen Datenfluss berücksichtigen:You can bind through any number of nested components, but you must respect the one-way flow of data:

  • Änderungsbenachrichtigungen durchlaufen die Hierarchie von unten nach oben.Change notifications flow up the hierarchy.
  • Neue Parameterwerte durchlaufen die Hierarchie von oben nach unten.New parameter values flow down the hierarchy.

Ein gängiges und empfohlenes Verfahren besteht darin, nur die zugrunde liegenden Daten in der übergeordneten Komponente zu speichern, damit Sie immer genau wissen, welcher Zustand aktualisiert werden muss.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.

Die folgenden Komponenten veranschaulichen die oben beschriebenen Konzepte: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}");
    }
}

Einen alternativen Ansatz, der sich für die arbeitsspeicherinterne und komponentenübergreifende Datenfreigabe eignet (Komponenten müssen nicht unbedingt geschachtelt sein), finden Sie im Abschnitt In-Memory-Zustandscontainerdienst unter Blazor-Zustandsverwaltung in ASP.NET Core.For an alternative approach suited to sharing data in-memory across components that aren't necessarily nested, see the In-memory state container service section of Blazor-Zustandsverwaltung in ASP.NET Core.

Zusätzliche RessourcenAdditional resources