Blazor data binding ASP.NET Core

Questo articolo illustra le funzionalità di data binding per Razor i componenti e gli elementi DOM (Document Object Model) nelle Blazor app.

Razor i componenti forniscono funzionalità di data binding con l'attributo @bindRazor di direttiva con un campo, una proprietà o Razor un valore di espressione.

L'esempio seguente esegue l'associazione:

  • Valore <input> dell'elemento per il campo C# inputValue .
  • <input> Secondo valore dell'elemento per la proprietà C#InputValue.

Quando un <input> elemento perde lo stato attivo, viene aggiornato il campo o la proprietà associata.

Pages/Bind.razor:

@page "/bind"

<p>
    <input @bind="inputValue" />
</p>

<p>
    <input @bind="InputValue" />
</p>

<ul>
    <li><code>inputValue</code>: @inputValue</li>
    <li><code>InputValue</code>: @InputValue</li>
</ul>

@code {
    private string? inputValue;

    private string? InputValue { get; set; }
}

La casella di testo viene aggiornata nell'interfaccia utente solo quando viene eseguito il rendering del componente, non in risposta alla modifica del valore del campo o della proprietà. Poiché i componenti vengono visualizzati dopo l'esecuzione del codice del gestore eventi, gli aggiornamenti dei campi e delle proprietà vengono in genere riflessi nell'interfaccia utente immediatamente dopo l'attivazione di un gestore eventi.

Come dimostrazione della composizione del data binding in HTML, nell'esempio seguente la InputValue proprietà viene associata agli attributi e onchange del value secondo <input> elemento (change). Il secondo <input> elemento nell'esempio seguente è una dimostrazione di concetto e non è progettato per suggerire come associare i dati nei Razor componenti.

Pages/BindTheory.razor:

@page "/bind-theory"

<p>
    <label>
        Normal Blazor binding: 
        <input @bind="InputValue" />
    </label>
</p>

<p>
    <label>
        Demonstration of equivalent HTML binding: 
        <input value="@InputValue"
            @onchange="@((ChangeEventArgs __e) => InputValue = __e?.Value?.ToString())" />
    </label>
</p>

<p>
    <code>InputValue</code>: @InputValue
</p>

@code {
    private string? InputValue { get; set; }
}

Quando viene eseguito il rendering del BindTheory componente, l'oggetto value dell'elemento dimostrativo <input> HTML proviene dalla InputValue proprietà . Quando l'utente immette un valore nella casella di testo e modifica lo stato attivo dell'elemento, l'evento onchange viene generato e la InputValue proprietà viene impostata sul valore modificato. In realtà, l'esecuzione del codice è più complessa perché @bind gestisce i casi in cui vengono eseguite le conversioni dei tipi. In generale, @bind associa il valore corrente di un'espressione all'attributo value di <input> e gestisce le modifiche usando il gestore registrato.

Associare una proprietà o un campo su altri eventi DOM (Document Object Model) includendo un attributo con un @bind:event="{EVENT}" evento DOM per il {EVENT} segnaposto. Nell'esempio seguente la InputValue proprietà viene associata al <input> valore dell'elemento quando viene attivato l'evento dell'elemento oninput (input). A differenza dell'evento onchange (change), che viene generato quando l'elemento perde lo stato attivo, oninput (input) viene attivato quando il valore della casella di testo cambia.

Page/BindEvent.razor:

@page "/bind-event"

<p>
    <input @bind="InputValue" @bind:event="oninput" />
</p>

<p>
    <code>InputValue</code>: @InputValue
</p>

@code {
    private string? InputValue { get; set; }
}

Per eseguire la logica asincrona dopo l'associazione, usare @bind:after="{EVENT}" con un evento DOM per il {EVENT} segnaposto. Un metodo C# assegnato non viene eseguito finché il valore associato non viene assegnato in modo sincrono.

L'uso di un parametro di callback di eventi ([Parameter] public EventCallback<string> ValueChanged { get; set; }) con @bind:after non è supportato. Passare invece un metodo che restituisce un oggetto Action o Task a @bind:after.

Nell'esempio seguente:

  • L'elemento <input>value è associato al valore di searchText in modo sincrono.
  • Dopo ogni sequenza di tasti (onchange evento) nel campo, il PerformSearch metodo viene eseguito in modo asincrono.
  • PerformSearch chiama un servizio con un metodo asincrono (FetchAsync) per restituire i risultati della ricerca.
@inject ISearchService SearchService

<input @bind="searchText" @bind:after="PerformSearch" />

@code {
    private string? searchText;
    private string[]? searchResult;

    private async Task PerformSearch()
    {
        searchResult = await SearchService.FetchAsync(searchText);
    }
}

Esempi aggiuntivi

Pages/BindAfter.razor:

@page "/bind-after"
@using Microsoft.AspNetCore.Components.Forms

<h1>Bind After Examples</h1>

<h2>Elements</h2>

<input type="text" @bind="text" @bind:after="() => { }" />

<input type="text" @bind="text" @bind:after="After" />

<input type="text" @bind="text" @bind:after="AfterAsync" />

<h2>Components</h2>

<InputText @bind-Value="text" @bind-Value:after="() => { }" />

<InputText @bind-Value="text" @bind-Value:after="After" />

<InputText @bind-Value="text" @bind-Value:after="AfterAsync" />

@code {
    private string text = "";

    private void After() {}
    private Task AfterAsync() { return Task.CompletedTask; }
}

Per altre informazioni sul InputText componente, vedere ASP.NET Core Blazor moduli e componenti di input.

I componenti supportano il data binding bidirezionale definendo una coppia di parametri:

  • @bind:get: specifica il valore da associare.
  • @bind:set: specifica un callback per quando il valore viene modificato.

I @bind:get modificatori e @bind:set vengono sempre usati insieme.

L'uso di un parametro di callback di eventi con @bind:set ([Parameter] public EventCallback<string> ValueChanged { get; set; }) non è supportato. Passare invece un metodo che restituisce un oggetto Action o Task a @bind:set.

Esempio

Pages/BindGetSet.razor:

@page "/bind-get-set"
@using Microsoft.AspNetCore.Components.Forms

<h1>Bind Get Set Examples</h1>

<h2>Elements</h2>

<input type="text" @bind:get="text" @bind:set="(value) => { text = value; }" />
<input type="text" @bind:get="text" @bind:set="Set" />
<input type="text" @bind:get="text" @bind:set="SetAsync" />

<h2>Components</h2>

<InputText @bind-Value:get="text" @bind-Value:set="(value) => { text = value; }" />
<InputText @bind-Value:get="text" @bind-Value:set="Set" />
<InputText @bind-Value:get="text" @bind-Value:set="SetAsync" />

@code {
    private string text = "";

    private void Set(string value)
    {
        text = value;
    }

    private Task SetAsync(string value)
    {
        text = value;
        return Task.CompletedTask;
    }
}

Per altre informazioni sul InputText componente, vedere ASP.NET Core Blazor moduli e componenti di input.

Razor l'associazione di attributi fa distinzione tra maiuscole e minuscole:

  • @bind, @bind:evente @bind:after sono validi.
  • @Bind/@bind:Event/@bind:aftEr(lettere maiuscole) o @BIND/@BIND:AFTER/@BIND:EVENT(tutte le lettere maiuscole) non sono valide.

Usare @bind:get/@bind:set modificatori ed evitare gestori eventi per il data binding bidirezionale

Il data binding bidirezionale non è possibile implementare con un gestore eventi. Usare @bind:get/@bind:set i modificatori per il data binding bidirezionale.

Si consideri l'approccio disfunzionale seguente per il data binding bidirezionale usando un gestore eventi:

<p>
    <input value="@inputValue" @oninput="OnInput" />
</p>

<p>
    <code>inputValue</code>: @inputValue
</p>

@code {
    private string? inputValue;

    private void OnInput(ChangeEventArgs args)
    {
        var newValue = args.Value?.ToString() ?? string.Empty;

        inputValue = newValue.Length > 4 ? "Long!" : newValue;
    }
}

Il OnInput gestore eventi aggiorna il valore di inputValue su Long! dopo che viene fornito un quarto carattere. Tuttavia, l'utente può continuare ad aggiungere caratteri al valore dell'elemento nell'interfaccia utente. Il valore di inputValue non è associato al valore dell'elemento con ogni sequenza di tasti. L'esempio precedente è in grado di eseguire il data binding unidirezionale.

Il motivo di questo comportamento è che Blazor non è consapevole del fatto che il codice intende modificare il valore di inputValue nel gestore eventi. Blazor non tenta di forzare i valori degli elementi DOM (Document Object Model) e i valori delle variabili .NET in modo che corrispondano a meno che non siano associati alla @bind sintassi. Nelle versioni precedenti di Blazor, il data binding bidirezionale viene implementato associando l'elemento a una proprietà e controllando il valore della proprietà con il relativo setter. In ASP.NET Core 7.0 o versione successiva viene @bind:get/@bind:set usata la sintassi del modificatore per implementare il data binding bidirezionale, come illustrato nell'esempio seguente.

Si consideri l'approccio corretto seguente usando @bind:get/@bind:set per il data binding bidirezionale:

<p>
    <input @bind:event="oninput" @bind:get="inputValue" @bind:set="OnInput" />
</p>

<p>
    <code>inputValue</code>: @inputValue
</p>

@code {
    private string? inputValue;

    private void OnInput(string value)
    {
        var newValue = value ?? string.Empty;

        inputValue = newValue.Length > 4 ? "Long!" : newValue;
    }
}

L'uso dei @bind:get/@bind:set modificatori controlla entrambi il valore sottostante di inputValue tramite @bind:set e associa il valore di inputValue al valore dell'elemento tramite .@bind:get Nell'esempio precedente viene illustrato l'approccio corretto per l'implementazione del data binding bidirezionale.

Associazione a una proprietà con C# get e set funzioni di accesso

Le funzioni di accesso e set C# get possono essere usate per creare un comportamento di formato di associazione personalizzato, come illustrato nel componente seguenteDecimalBinding. Il componente associa un decimale positivo o negativo con un massimo di tre posizioni decimali a un <input> elemento tramite una string proprietà (DecimalValue).

Pages/DecimalBinding.razor:

@page "/decimal-binding"
@using System.Globalization

<p>
    <label>
        Decimal value (±0.000 format):
        <input @bind="DecimalValue" />
    </label>
</p>

<p>
    <code>decimalValue</code>: @decimalValue
</p>

@code {
    private decimal decimalValue = 1.1M;
    private NumberStyles style = 
        NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
    private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");

    private string DecimalValue
    {
        get => decimalValue.ToString("0.000", culture);
        set
        {
            if (Decimal.TryParse(value, style, culture, out var number))
            {
                decimalValue = Math.Round(number, 3);
            }
        }
    }
}

Nota

L'associazione bidirezionale a una proprietà con get/set funzioni di accesso richiede l'eliminazione dell'oggetto Task restituito da .EventCallback.InvokeAsync Per il data binding bidirezionale in ASP.NET Core 7.0 o versione successiva, è consigliabile usare @bind:get/@bind:set i modificatori. Per altre informazioni, vedere le @bind:get/@bind:set linee guida nella sezione di apertura di questo articolo.

Selezione di più opzioni con <select> elementi

L'associazione supporta la multiple selezione di opzioni con <select> elementi. L'evento @onchange fornisce una matrice degli elementi selezionati tramite argomenti evento (ChangeEventArgs). Il valore deve essere associato a un tipo di matrice.

Pages/BindMultipleInput.razor:

@page "/bind-multiple-input"

<h1>Bind Multiple <code>input</code>Example</h1>

<p>
    <label>
        Select one or more cars: 
        <select @onchange="SelectedCarsChanged" multiple>
            <option value="audi">Audi</option>
            <option value="jeep">Jeep</option>
            <option value="opel">Opel</option>
            <option value="saab">Saab</option>
            <option value="volvo">Volvo</option>
        </select>
    </label>
</p>

<p>
    Selected Cars: @string.Join(", ", SelectedCars)
</p>

<p>
    <label>
        Select one or more cities: 
        <select @bind="SelectedCities" multiple>
            <option value="bal">Baltimore</option>
            <option value="la">Los Angeles</option>
            <option value="pdx">Portland</option>
            <option value="sf">San Francisco</option>
            <option value="sea">Seattle</option>
        </select>
    </label>
</p>

<span>
    Selected Cities: @string.Join(", ", SelectedCities)
</span>

@code {
    public string[] SelectedCars { get; set; } = new string[] { };
    public string[] SelectedCities { get; set; } = new[] { "bal", "sea" };

    private void SelectedCarsChanged(ChangeEventArgs e)
    {
        if (e.Value is not null)
        {
            SelectedCars = (string[])e.Value;
        }
    }
}

Per informazioni sulla modalità di gestione delle stringhe e dei valori vuoti nel data binding, vedere la sezione Binding element options to C# object values .For information on how empty strings and null values are handled in data binding, see the Binding <select> element options to C# object null values section.

Opzioni dell'elemento di <select> associazione ai valori dell'oggetto null C#

Non esiste un modo ragionevole per rappresentare un <select> valore dell'opzione elemento come valore dell'oggetto null C#, perché:

  • Gli attributi HTML non possono avere null valori. L'equivalente più vicino a null in HTML è l'assenza dell'attributo HTML value dall'elemento <option> .
  • Quando si seleziona un <option> oggetto senza value attributo, il browser considera il valore come contenuto di testo dell'elemento <option>.

Il Blazor framework non tenta di eliminare il comportamento predefinito perché comporta:

  • Creazione di una catena di soluzioni alternative speciali nel framework.
  • Modifiche di rilievo al comportamento corrente del framework.

L'equivalente più plausibile null in HTML è una stringavalue vuota. Il Blazor framework gestisce null le conversioni di stringhe vuote per l'associazione bidirezionale al valore di un <select>.

Valori non analizzabili

Quando un utente fornisce un valore non verificabile a un elemento databound, il valore non analizzabile viene ripristinato automaticamente al valore precedente quando viene attivato l'evento di associazione.

Si consideri il componente seguente, in cui un <input> elemento è associato a un int tipo con un valore iniziale di 123.

Pages/UnparsableValues.razor:

@page "/unparseable-values"

<p>
    <input @bind="inputValue" />
</p>

<p>
    <code>inputValue</code>: @inputValue
</p>

@code {
    private int inputValue = 123;
}

Per impostazione predefinita, l'associazione si applica all'evento dell'elemento onchange . Se l'utente aggiorna il valore della voce della casella di testo in 123.45 e modifica lo stato attivo, il valore dell'elemento viene ripristinato 123 quando onchange viene attivato. Quando il valore 123.45 viene rifiutato a favore del valore originale di 123, l'utente riconosce che il valore non è stato accettato.

Per l'evento oninput (@bind:event="oninput"), si verifica una riversione del valore dopo qualsiasi sequenza di tasti che introduce un valore non verificabile. Quando si specifica come destinazione l'evento oninput con un inttipo associato a , un utente non può digitare un carattere punto (.). Un carattere punto (.) viene immediatamente rimosso, quindi l'utente riceve un feedback immediato che è consentito solo per numeri interi. Esistono scenari in cui il ripristino del valore sull'evento oninput non è ideale, ad esempio quando l'utente deve essere autorizzato a cancellare un valore non verificabile <input> . Le alternative includono:

  • Non usare l'evento oninput . Usare l'evento predefinito onchange , in cui un valore non valido non viene ripristinato fino a quando l'elemento non perde lo stato attivo.
  • Eseguire il binding a un tipo nullable, ad esempio o e usare @bind:get@bind:set/modificatori (descritti in precedenza in questo articolo) o eseguire l'associazione a una proprietà con logica personalizzata get e set della funzione di accesso per gestire voci non valide.stringint?
  • Usare un componente di convalida dei moduli, ad esempio InputNumber<TValue> o .InputDate<TValue> I componenti di convalida dei moduli forniscono il supporto predefinito per gestire gli input non validi. Componenti di convalida dei moduli:
    • Consentire all'utente di fornire input non valido e ricevere errori di convalida nell'oggetto associato EditContext.
    • Visualizza gli errori di convalida nell'interfaccia utente senza interferire con l'utente immettendo dati webform aggiuntivi.

Stringhe di formato

Il data binding funziona con una singola DateTime stringa di formato usando @bind:format="{FORMAT STRING}", dove il {FORMAT STRING} segnaposto è la stringa di formato. Altre espressioni di formato, ad esempio i formati valuta o numero, non sono disponibili in questo momento, ma potrebbero essere aggiunte in una versione futura.

Pages/DateBinding.razor:

@page "/date-binding"

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

<p>
    <code>startDate</code>: @startDate
</p>

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

Nel codice precedente il <input> tipo di campo dell'elemento (type attributo) è predefinito su text.

Nullable System.DateTime e System.DateTimeOffset sono supportati:

private DateTime? date;
private DateTimeOffset? dateOffset;

Se si specifica un formato per il date tipo di campo, non è consigliabile perché Blazor è disponibile il supporto predefinito per formattare le date. Nonostante la raccomandazione, usare solo il formato data per l'associazione yyyy-MM-dd per funzionare correttamente se viene fornito un formato con il date tipo di campo:

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

Associazione con parametri del componente

Uno scenario comune associa una proprietà di un componente figlio a una proprietà nel relativo componente padre. Questo scenario viene chiamato associazione concatenata perché si verificano simultaneamente più livelli di associazione.

I parametri del componente consentono le proprietà di associazione di un componente padre con @bind-{PROPERTY} sintassi, in cui il {PROPERTY} segnaposto è la proprietà da associare.

Non è possibile implementare associazioni concatenati con @bind sintassi nel componente figlio. Un gestore eventi e un valore devono essere specificati separatamente per supportare l'aggiornamento della proprietà nel componente padre dal componente figlio.

Il componente padre sfrutta ancora la @bind sintassi per configurare l'associazione dei dati con il componente figlio.

Il componente seguente ChildBind include un Year parametro componente e un EventCallback<TValue>oggetto . Per convenzione, il EventCallback<TValue> parametro per il parametro deve essere denominato come nome del parametro del componente con un suffisso "Changed". La sintassi di denominazione è , dove il segnaposto è {PARAMETER NAME}Changedil {PARAMETER NAME} nome del parametro. Nell'esempio seguente viene EventCallback<TValue> denominato YearChanged.

EventCallback.InvokeAsync richiama il delegato associato all'associazione con l'argomento specificato e invia una notifica evento per la proprietà modificata.

Shared/ChildBind.razor:

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

@code {
    private Random r = new();

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

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

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

Per altre informazioni sugli eventi e EventCallback<TValue>, vedere la sezione EventCallback dell'articolo sulla gestione degli eventi ASP.NET CoreBlazor.

Nel componente seguente Parent1 il year campo è associato al Year parametro del componente figlio. Il Year parametro è associabile perché ha un evento complementare YearChanged che corrisponde al tipo del Year parametro.

Pages/Parent1.razor:

@page "/parent-1"

<h1>Parent Component</h1>

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

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

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

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

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

L'associazione dei parametri del componente può anche attivare @bind:after eventi. Nell'esempio seguente il YearUpdated metodo viene eseguito in modo asincrono dopo l'associazione del Year parametro del componente.

<ChildBind @bind-Year="year" @bind-Year:after="YearUpdated" />

@code {
    ...

    private async Task YearUpdated()
    {
        ... = await ...;
    }
}

Per convenzione, una proprietà può essere associata a un gestore eventi corrispondente includendo un @bind-{PROPERTY}:event attributo assegnato al gestore, dove il {PROPERTY} segnaposto è la proprietà. <ChildBind @bind-Year="year" /> equivale alla scrittura:

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

In un esempio più sofisticato e reale, il componente seguente PasswordEntry :

  • Imposta il valore di un <input> elemento su un password campo.
  • Espone le modifiche di una proprietà a un Password componente padre con un EventCallback oggetto che passa il valore corrente del campo figlio password come argomento.
  • Usa l'evento per attivare il onclickToggleShowPassword metodo. Per altre informazioni, vedere gestione degli eventi ASP.NET CoreBlazor.

Shared/PasswordEntry.razor:

<div class="card bg-light mt-3" style="width:22rem ">
    <div class="card-body">
        <h3 class="card-title">Password Component</h3>
        <p class="card-text">
            <label>
                Password:
                <input @oninput="OnPasswordChanged"
                       required
                       type="@(showPassword ? "text" : "password")"
                       value="@password" />
            </label>
        </p>
        <button class="btn btn-primary" @onclick="ToggleShowPassword">
            Show password
        </button>
    </div>
</div>

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

Il PasswordEntry componente viene usato in un altro componente, ad esempio l'esempio di componente seguente PasswordBinding .

Pages/PasswordBinding.razor:

@page "/password-binding"

<h1>Password Binding</h1>

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

<p>
    <code>password</code>: @password
</p>

@code {
    private string password = "Not set";
}

Quando il componente viene inizialmente eseguito il PasswordBinding rendering, il password valore di Not set viene visualizzato nell'interfaccia utente. Dopo il rendering iniziale, il valore di password riflette le modifiche apportate al Password valore del parametro del componente nel PasswordEntry componente.

Nota

L'esempio precedente associa la password unidirezionale dal componente figlio PasswordEntry al componente padre PasswordBinding . L'associazione bidirezionale non è un requisito in questo scenario se l'obiettivo è che l'app disponga di un componente di immissione della password condivisa per il riutilizzo dell'app che passa semplicemente la password all'elemento padre. Per un approccio che consente l'associazione bidirezionale senza scrivere direttamente nel parametro del componente figlio, vedere l'esempioNestedChild del componente nella sezione Bind in più di due componenti di questo articolo.

Eseguire controlli o errori di trappola nel gestore. Il componente modificato PasswordEntry seguente fornisce commenti e suggerimenti immediati all'utente se viene usato uno spazio nel valore della password.

Shared/PasswordEntry.razor:

<div class="card bg-light mt-3" style="width:22rem ">
    <div class="card-body">
        <h3 class="card-title">Password Component</h3>
        <p class="card-text">
            <label>
                Password:
                <input @oninput="OnPasswordChanged"
                       required
                       type="@(showPassword ? "text" : "password")"
                       value="@password" />
            </label>
            <span class="text-danger">@validationMessage</span>
        </p>
        <button class="btn btn-primary" @onclick="ToggleShowPassword">
            Show password
        </button>
    </div>
</div>

@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 != null && password.Contains(' '))
        {
            validationMessage = "Spaces not allowed!";

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

            return PasswordChanged.InvokeAsync(password);
        }
    }

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

Nell'esempio seguente il PasswordUpdated metodo viene eseguito in modo asincrono dopo l'associazione del Password parametro del componente:

<PasswordEntry @bind-Password="password" @bind-Password:after="PasswordUpdated" />

Associare più di due componenti

È possibile associare i parametri tramite qualsiasi numero di componenti annidati, ma è necessario rispettare il flusso unidirezionale dei dati:

  • Le notifiche di modifica passano alla gerarchia.
  • I nuovi valori dei parametri passano verso il basso nella gerarchia.

Un approccio comune e consigliato consiste nell'archiviare solo i dati sottostanti nel componente padre per evitare qualsiasi confusione sullo stato da aggiornare, come illustrato nell'esempio seguente.

Pages/Parent2.razor:

@page "/parent-2"

<h1>Parent Component</h1>

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

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

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

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

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

Nel componente seguente NestedChild , il NestedGrandchild componente:

  • Assegna il valore di ChildMessage a GrandchildMessage con @bind:get sintassi.
  • GrandchildMessage Aggiornamenti quando ChildMessageChanged viene eseguito con @bind:set la sintassi.

Shared/NestedChild.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>

    <NestedGrandchild @bind-GrandchildMessage:get="ChildMessage" 
        @bind-GrandchildMessage:set="ChildMessageChanged" />
</div>

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

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

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

Shared/NestedGrandchild.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}");
    }
}

Per un approccio alternativo adatto alla condivisione dei dati in memoria e tra i componenti che non sono necessariamente annidati, vedere ASP.NET Core Blazor gestione dello stato.

Risorse aggiuntive

Razor i componenti forniscono funzionalità di data binding con l'attributo @bindRazor direttiva con un campo, una proprietà o Razor un valore di espressione.

L'esempio seguente associa:

  • Valore <input> dell'elemento nel campo C# inputValue .
  • Un secondo <input> valore dell'elemento nella proprietà C# InputValue .

Quando un <input> elemento perde lo stato attivo, viene aggiornato il campo o la proprietà associata.

Pages/Bind.razor:

@page "/bind"

<p>
    <input @bind="inputValue" />
</p>

<p>
    <input @bind="InputValue" />
</p>

<ul>
    <li><code>inputValue</code>: @inputValue</li>
    <li><code>InputValue</code>: @InputValue</li>
</ul>

@code {
    private string? inputValue;

    private string? InputValue { get; set; }
}

La casella di testo viene aggiornata nell'interfaccia utente solo quando il componente viene eseguito il rendering, non in risposta alla modifica del valore del campo o della proprietà. Poiché i componenti vengono visualizzati dopo l'esecuzione del codice del gestore eventi, gli aggiornamenti dei campi e delle proprietà vengono in genere riflessi nell'interfaccia utente immediatamente dopo l'attivazione di un gestore eventi.

Come dimostrazione del modo in cui il data binding si compone in HTML, nell'esempio seguente viene associata la InputValue proprietà agli attributi e onchange agli attributi del value secondo <input> elemento (change). Il secondo <input> elemento nell'esempio seguente è una dimostrazione di concetto e non è destinato a suggerire come associare i dati nei Razor componenti.

Pages/BindTheory.razor:

@page "/bind-theory"

<p>
    <label>
        Normal Blazor binding: 
        <input @bind="InputValue" />
    </label>
</p>

<p>
    <label>
        Demonstration of equivalent HTML binding: 
        <input value="@InputValue"
            @onchange="@((ChangeEventArgs __e) => InputValue = __e?.Value?.ToString())" />
    </label>
</p>

<p>
    <code>InputValue</code>: @InputValue
</p>

@code {
    private string? InputValue { get; set; }
}

Quando viene eseguito il rendering del componente, l'elemento BindTheoryvalue dimostrativo <input> HTML proviene dalla InputValue proprietà . Quando l'utente immette un valore nella casella di testo e modifica lo stato attivo dell'elemento, l'evento onchange viene attivato e la InputValue proprietà viene impostata sul valore modificato. In realtà, l'esecuzione del codice è più complessa perché @bind gestisce i casi in cui vengono eseguite le conversioni dei tipi. In generale, @bind associa il valore corrente di un'espressione all'attributo dell'oggetto value<input> e gestisce le modifiche usando il gestore registrato.

Associare una proprietà o un campo ad altri eventi DOM (Document Object Model) includendo un attributo con un @bind:event="{EVENT}" evento DOM per il {EVENT} segnaposto. Nell'esempio seguente la InputValue proprietà viene associata al <input> valore dell'elemento oninput quando viene attivato l'evento dell'elemento (input). A differenza dell'evento (change), che viene generato quando l'elemento onchange perde lo stato attivo, oninput (input) viene generato quando il valore della casella di testo cambia.

Page/BindEvent.razor:

@page "/bind-event"

<p>
    <input @bind="InputValue" @bind:event="oninput" />
</p>

<p>
    <code>InputValue</code>: @InputValue
</p>

@code {
    private string? InputValue { get; set; }
}

Razor l'associazione di attributi è distinzione tra maiuscole e minuscole:

  • @bind e @bind:event sono validi.
  • @Bind/@Bind:Event(lettere maiuscole e E) o@BIND:EVENT@BIND/(tutte le lettere B maiuscole) non sono valide.

Selezione di più opzioni con <select> elementi

L'associazione supporta la multiple selezione delle opzioni con <select> elementi. L'evento @onchange fornisce una matrice degli elementi selezionati tramite argomenti evento (ChangeEventArgs). Il valore deve essere associato a un tipo di matrice.

Pages/BindMultipleInput.razor:

@page "/bind-multiple-input"

<h1>Bind Multiple <code>input</code>Example</h1>

<p>
    <label>
        Select one or more cars: 
        <select @onchange="SelectedCarsChanged" multiple>
            <option value="audi">Audi</option>
            <option value="jeep">Jeep</option>
            <option value="opel">Opel</option>
            <option value="saab">Saab</option>
            <option value="volvo">Volvo</option>
        </select>
    </label>
</p>

<p>
    Selected Cars: @string.Join(", ", SelectedCars)
</p>

<p>
    <label>
        Select one or more cities: 
        <select @bind="SelectedCities" multiple>
            <option value="bal">Baltimore</option>
            <option value="la">Los Angeles</option>
            <option value="pdx">Portland</option>
            <option value="sf">San Francisco</option>
            <option value="sea">Seattle</option>
        </select>
    </label>
</p>

<span>
    Selected Cities: @string.Join(", ", SelectedCities)
</span>

@code {
    public string[] SelectedCars { get; set; } = new string[] { };
    public string[] SelectedCities { get; set; } = new[] { "bal", "sea" };

    private void SelectedCarsChanged(ChangeEventArgs e)
    {
        if (e.Value is not null)
        {
            SelectedCars = (string[])e.Value;
        }
    }
}

Per informazioni sul modo in cui vengono gestite stringhe e null valori vuoti nel data binding, vedere la sezione Opzioni dell'elemento Binding <select> ai valori degli oggetti null C# .

Opzioni dell'elemento di associazione <select> ai valori degli oggetti null C#

Non è possibile rappresentare un <select> valore dell'opzione di elemento come valore dell'oggetto null C# perché:

  • Gli attributi HTML non possono avere null valori. L'equivalente più vicino a null in HTML è assenza dell'attributo HTML value dall'elemento <option> .
  • Quando si seleziona un oggetto <option> senza value attributo, il browser considera il valore come contenuto di testo di tale <option>elemento.

Il Blazor framework non tenta di eliminare il comportamento predefinito perché implica:

  • Creazione di una catena di soluzioni alternative speciali nel framework.
  • Modifiche di rilievo al comportamento corrente del framework.

L'equivalente più plausibile null in HTML è una stringavalue vuota. Il Blazor framework gestisce null le conversioni di stringhe vuote per l'associazione bidirezionale al valore di un <select>oggetto .

Valori non riparabili

Quando un utente fornisce un valore non verificabile a un elemento in ingresso dati, il valore non verificabile viene automaticamente ripristinato al relativo valore precedente quando viene attivato l'evento di associazione.

Prendere in considerazione il componente seguente, in cui un <input> elemento è associato a un tipo con un int valore iniziale di 123.

Pages/UnparsableValues.razor:

@page "/unparseable-values"

<p>
    <input @bind="inputValue" />
</p>

<p>
    <code>inputValue</code>: @inputValue
</p>

@code {
    private int inputValue = 123;
}

Per impostazione predefinita, l'associazione si applica all'evento dell'elemento onchange . Se l'utente aggiorna il valore della voce della casella di testo in 123.45 e modifica lo stato attivo, il valore dell'elemento viene ripristinato 123 quando onchange viene generato. Quando il valore viene rifiutato a favore del valore 123.45 originale di 123, l'utente riconosce che il valore non è stato accettato.

Per l'evento oninput (@bind:event="oninput"), si verifica una riversione del valore dopo qualsiasi sequenza di tasti che introduce un valore non verificabile. Quando si punta all'evento oninput con un tipo associato, un utente non può digitare un intcarattere punto (.). Un carattere dot (.) viene rimosso immediatamente, quindi l'utente riceve un feedback immediato che solo i numeri interi sono consentiti. Esistono scenari in cui il ripristino del valore dell'evento oninput non è ideale, ad esempio quando l'utente deve essere autorizzato a cancellare un valore non verificabile <input> . Le alternative includono:

  • Non usare l'evento oninput . Usare l'evento predefinito onchange , in cui un valore non valido non viene ripristinato finché l'elemento non perde lo stato attivo.
  • Eseguire il binding a un tipo nullable, ad esempio int? o string e fornire l'associazione a una proprietà con logica personalizzata get e set di accesso per gestire voci non valide.
  • Usare un componente di convalida del modulo, ad InputNumber<TValue> esempio o InputDate<TValue>. I componenti di convalida dei moduli forniscono il supporto predefinito per gestire gli input non validi. Componenti di convalida del modulo:
    • Consentire all'utente di specificare errori di input non validi e ricevere errori di convalida nell'oggetto associato EditContext.
    • Visualizzare gli errori di convalida nell'interfaccia utente senza interferire con l'utente immettendo dati webform aggiuntivi.

Stringhe di formato

Il data binding funziona con una singola DateTime stringa di formato usando @bind:format="{FORMAT STRING}", dove il segnaposto è la {FORMAT STRING} stringa di formato. Altre espressioni di formato, ad esempio i formati valuta o numero, non sono disponibili in questo momento, ma potrebbero essere aggiunte in una versione futura.

Pages/DateBinding.razor:

@page "/date-binding"

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

<p>
    <code>startDate</code>: @startDate
</p>

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

Nel codice precedente il <input> tipo di campo dell'elemento (type attributo) è predefinito su text.

Nullable System.DateTime e System.DateTimeOffset sono supportati:

private DateTime? date;
private DateTimeOffset? dateOffset;

Se si specifica un formato per il date tipo di campo, non è consigliabile perché Blazor è disponibile il supporto predefinito per formattare le date. Nonostante la raccomandazione, usare solo il formato data per l'associazione yyyy-MM-dd per funzionare correttamente se viene fornito un formato con il date tipo di campo:

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

Associazione a una proprietà con C# get e set funzioni di accesso

Le funzioni di accesso e set C# get possono essere usate per creare un comportamento di formato di associazione personalizzato, come illustrato nel componente seguenteDecimalBinding. Il componente associa un decimale positivo o negativo con un massimo di tre cifre decimali a un <input> elemento tramite una string proprietà (DecimalValue).

Pages/DecimalBinding.razor:

@page "/decimal-binding"
@using System.Globalization

<p>
    <label>
        Decimal value (±0.000 format):
        <input @bind="DecimalValue" />
    </label>
</p>

<p>
    <code>decimalValue</code>: @decimalValue
</p>

@code {
    private decimal decimalValue = 1.1M;
    private NumberStyles style = 
        NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
    private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");

    private string DecimalValue
    {
        get => decimalValue.ToString("0.000", culture);
        set
        {
            if (Decimal.TryParse(value, style, culture, out var number))
            {
                decimalValue = Math.Round(number, 3);
            }
        }
    }
}

Associazione con parametri del componente

Uno scenario comune associa una proprietà di un componente figlio a una proprietà nel relativo componente padre. Questo scenario viene chiamato associazione concatenata perché si verificano simultaneamente più livelli di associazione.

I parametri del componente consentono le proprietà di associazione di un componente padre con @bind-{PROPERTY} sintassi, in cui il {PROPERTY} segnaposto è la proprietà da associare.

Non è possibile implementare associazioni concatenati con @bind sintassi nel componente figlio. Un gestore eventi e un valore devono essere specificati separatamente per supportare l'aggiornamento della proprietà nel componente padre dal componente figlio.

Il componente padre sfrutta ancora la @bind sintassi per configurare l'associazione dei dati con il componente figlio.

Il componente seguente ChildBind include un Year parametro componente e un EventCallback<TValue>oggetto . Per convenzione, il EventCallback<TValue> parametro per il parametro deve essere denominato come nome del parametro del componente con un suffisso "Changed". La sintassi di denominazione è , dove il segnaposto è {PARAMETER NAME}Changedil {PARAMETER NAME} nome del parametro. Nell'esempio seguente viene EventCallback<TValue> denominato YearChanged.

EventCallback.InvokeAsync richiama il delegato associato all'associazione con l'argomento specificato e invia una notifica evento per la proprietà modificata.

Shared/ChildBind.razor:

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

@code {
    private Random r = new();

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

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

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

Per altre informazioni sugli eventi e EventCallback<TValue>, vedere la sezione EventCallback dell'articolo sulla gestione degli eventi ASP.NET CoreBlazor.

Nel componente seguente Parent1 il year campo è associato al Year parametro del componente figlio. Il Year parametro è associabile perché ha un evento complementare YearChanged che corrisponde al tipo del Year parametro.

Pages/Parent1.razor:

@page "/parent-1"

<h1>Parent Component</h1>

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

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

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

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

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

Per convenzione, una proprietà può essere associata a un gestore eventi corrispondente includendo un @bind-{PROPERTY}:event attributo assegnato al gestore, dove il {PROPERTY} segnaposto è la proprietà. <ChildBind @bind-Year="year" /> equivale alla scrittura:

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

In un esempio più sofisticato e reale, il componente seguente PasswordEntry :

  • Imposta il valore di un <input> elemento su un password campo.
  • Espone le modifiche di una proprietà a un Password componente padre con un EventCallback oggetto che passa il valore corrente del campo figlio password come argomento.
  • Usa l'evento per attivare il onclickToggleShowPassword metodo. Per altre informazioni, vedere gestione degli eventi ASP.NET CoreBlazor.

Shared/PasswordEntry.razor:

<div class="card bg-light mt-3" style="width:22rem ">
    <div class="card-body">
        <h3 class="card-title">Password Component</h3>
        <p class="card-text">
            <label>
                Password:
                <input @oninput="OnPasswordChanged"
                       required
                       type="@(showPassword ? "text" : "password")"
                       value="@password" />
            </label>
        </p>
        <button class="btn btn-primary" @onclick="ToggleShowPassword">
            Show password
        </button>
    </div>
</div>

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

Il PasswordEntry componente viene usato in un altro componente, ad esempio l'esempio di componente seguente PasswordBinding .

Pages/PasswordBinding.razor:

@page "/password-binding"

<h1>Password Binding</h1>

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

<p>
    <code>password</code>: @password
</p>

@code {
    private string password = "Not set";
}

Quando il componente viene inizialmente eseguito il PasswordBinding rendering, il password valore di Not set viene visualizzato nell'interfaccia utente. Dopo il rendering iniziale, il valore di password riflette le modifiche apportate al Password valore del parametro del componente nel PasswordEntry componente.

Nota

L'esempio precedente associa la password unidirezionale dal componente figlio PasswordEntry al componente padre PasswordBinding . L'associazione bidirezionale non è un requisito in questo scenario se l'obiettivo è che l'app disponga di un componente di immissione della password condivisa per il riutilizzo dell'app che passa semplicemente la password all'elemento padre. Per un approccio che consente l'associazione bidirezionale senza scrivere direttamente nel parametro del componente figlio, vedere l'esempioNestedChild del componente nella sezione Bind in più di due componenti di questo articolo.

Eseguire controlli o errori di trappola nel gestore. Il componente modificato PasswordEntry seguente fornisce commenti e suggerimenti immediati all'utente se viene usato uno spazio nel valore della password.

Shared/PasswordEntry.razor:

<div class="card bg-light mt-3" style="width:22rem ">
    <div class="card-body">
        <h3 class="card-title">Password Component</h3>
        <p class="card-text">
            <label>
                Password:
                <input @oninput="OnPasswordChanged"
                       required
                       type="@(showPassword ? "text" : "password")"
                       value="@password" />
            </label>
            <span class="text-danger">@validationMessage</span>
        </p>
        <button class="btn btn-primary" @onclick="ToggleShowPassword">
            Show password
        </button>
    </div>
</div>

@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 != null && password.Contains(' '))
        {
            validationMessage = "Spaces not allowed!";

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

            return PasswordChanged.InvokeAsync(password);
        }
    }

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

Associare più di due componenti

È possibile associare i parametri tramite qualsiasi numero di componenti annidati, ma è necessario rispettare il flusso unidirezionale dei dati:

  • Le notifiche di modifica passano alla gerarchia.
  • I nuovi valori dei parametri passano verso il basso nella gerarchia.

Un approccio comune e consigliato consiste nell'archiviare solo i dati sottostanti nel componente padre per evitare qualsiasi confusione sullo stato da aggiornare, come illustrato nell'esempio seguente.

Pages/Parent2.razor:

@page "/parent-2"

<h1>Parent Component</h1>

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

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

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

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

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

Shared/NestedChild.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>

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

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

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

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

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

Avviso

In genere, evitare di creare componenti che scrivono direttamente nei propri parametri del componente. Il componente precedente NestedChild usa una BoundValue proprietà anziché scrivere direttamente nel parametro ChildMessage . Per altre informazioni, vedere componenti ASP.NET CoreRazor.

Shared/NestedGrandchild.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}");
    }
}

Per un approccio alternativo adatto alla condivisione dei dati in memoria e tra i componenti che non sono necessariamente annidati, vedere ASP.NET Core Blazor gestione dello stato.

Risorse aggiuntive

Razor i componenti forniscono funzionalità di data binding con l'attributo @bindRazor direttiva con un campo, una proprietà o Razor un valore di espressione.

L'esempio seguente associa:

  • Valore <input> dell'elemento nel campo C# inputValue .
  • Un secondo <input> valore dell'elemento nella proprietà C# InputValue .

Quando un <input> elemento perde lo stato attivo, viene aggiornato il campo o la proprietà associata.

Pages/Bind.razor:

@page "/bind"

<p>
    <input @bind="inputValue" />
</p>

<p>
    <input @bind="InputValue" />
</p>

<ul>
    <li><code>inputValue</code>: @inputValue</li>
    <li><code>InputValue</code>: @InputValue</li>
</ul>

@code {
    private string inputValue;

    private string InputValue { get; set; }
}

La casella di testo viene aggiornata nell'interfaccia utente solo quando il componente viene eseguito il rendering, non in risposta alla modifica del valore del campo o della proprietà. Poiché i componenti vengono visualizzati dopo l'esecuzione del codice del gestore eventi, gli aggiornamenti dei campi e delle proprietà vengono in genere riflessi nell'interfaccia utente immediatamente dopo l'attivazione di un gestore eventi.

Come dimostrazione del modo in cui il data binding si compone in HTML, nell'esempio seguente viene associata la InputValue proprietà agli attributi e onchange agli attributi del value secondo <input> elemento (change). Il secondo <input> elemento nell'esempio seguente è una dimostrazione di concetto e non è destinato a suggerire come associare i dati nei Razor componenti.

Pages/BindTheory.razor:

@page "/bind-theory"

<p>
    <label>
        Normal Blazor binding: 
        <input @bind="InputValue" />
    </label>
</p>

<p>
    <label>
        Demonstration of equivalent HTML binding: 
        <input value="@InputValue"
            @onchange="@((ChangeEventArgs __e) => InputValue = __e.Value.ToString())" />
    </label>
</p>

<p>
    <code>InputValue</code>: @InputValue
</p>

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

Quando viene eseguito il rendering del componente, l'elemento BindTheoryvalue dimostrativo <input> HTML proviene dalla InputValue proprietà . Quando l'utente immette un valore nella casella di testo e modifica lo stato attivo dell'elemento, l'evento onchange viene attivato e la InputValue proprietà viene impostata sul valore modificato. In realtà, l'esecuzione del codice è più complessa perché @bind gestisce i casi in cui vengono eseguite le conversioni dei tipi. In generale, @bind associa il valore corrente di un'espressione all'attributo dell'oggetto value<input> e gestisce le modifiche usando il gestore registrato.

Associare una proprietà o un campo ad altri eventi DOM (Document Object Model) includendo un attributo con un @bind:event="{EVENT}" evento DOM per il {EVENT} segnaposto. Nell'esempio seguente la InputValue proprietà viene associata al <input> valore dell'elemento oninput quando viene attivato l'evento dell'elemento (input). A differenza dell'evento (change), che viene generato quando l'elemento onchange perde lo stato attivo, oninput (input) viene generato quando il valore della casella di testo cambia.

Page/BindEvent.razor:

@page "/bind-event"

<p>
    <input @bind="InputValue" @bind:event="oninput" />
</p>

<p>
    <code>InputValue</code>: @InputValue
</p>

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

Razor l'associazione di attributi è distinzione tra maiuscole e minuscole:

  • @bind e @bind:event sono validi.
  • @Bind/@Bind:Event(lettere maiuscole e E) o@BIND:EVENT@BIND/(tutte le lettere B maiuscole) non sono valide.

Opzioni dell'elemento di associazione <select> ai valori degli oggetti null C#

Non è possibile rappresentare un <select> valore dell'opzione di elemento come valore dell'oggetto null C# perché:

  • Gli attributi HTML non possono avere null valori. L'equivalente più vicino a null in HTML è assenza dell'attributo HTML value dall'elemento <option> .
  • Quando si seleziona un oggetto <option> senza value attributo, il browser considera il valore come contenuto di testo di tale <option>elemento.

Il Blazor framework non tenta di eliminare il comportamento predefinito perché implica:

  • Creazione di una catena di soluzioni alternative speciali nel framework.
  • Modifiche di rilievo al comportamento corrente del framework.

L'equivalente più plausibile null in HTML è una stringavalue vuota. Il Blazor framework gestisce null le conversioni di stringhe vuote per l'associazione bidirezionale al valore di un <select>oggetto .

Valori non riparabili

Quando un utente fornisce un valore non verificabile a un elemento in ingresso dati, il valore non verificabile viene automaticamente ripristinato al relativo valore precedente quando viene attivato l'evento di associazione.

Prendere in considerazione il componente seguente, in cui un <input> elemento è associato a un tipo con un int valore iniziale di 123.

Pages/UnparsableValues.razor:

@page "/unparseable-values"

<p>
    <input @bind="inputValue" />
</p>

<p>
    <code>inputValue</code>: @inputValue
</p>

@code {
    private int inputValue = 123;
}

Per impostazione predefinita, l'associazione si applica all'evento dell'elemento onchange . Se l'utente aggiorna il valore della voce della casella di testo in 123.45 e modifica lo stato attivo, il valore dell'elemento viene ripristinato 123 quando onchange viene generato. Quando il valore viene rifiutato a favore del valore 123.45 originale di 123, l'utente riconosce che il valore non è stato accettato.

Per l'evento oninput (@bind:event="oninput"), si verifica una riversione del valore dopo qualsiasi sequenza di tasti che introduce un valore non verificabile. Quando si punta all'evento oninput con un tipo associato, un utente non può digitare un intcarattere punto (.). Un carattere dot (.) viene rimosso immediatamente, quindi l'utente riceve un feedback immediato che solo i numeri interi sono consentiti. Esistono scenari in cui il ripristino del valore dell'evento oninput non è ideale, ad esempio quando l'utente deve essere autorizzato a cancellare un valore non verificabile <input> . Le alternative includono:

  • Non usare l'evento oninput . Usare l'evento predefinito onchange , in cui un valore non valido non viene ripristinato finché l'elemento non perde lo stato attivo.
  • Eseguire il binding a un tipo nullable, ad esempio int? o string e fornire l'associazione a una proprietà con logica personalizzata get e set di accesso per gestire voci non valide.
  • Usare un componente di convalida del modulo, ad InputNumber<TValue> esempio o InputDate<TValue>. I componenti di convalida dei moduli forniscono il supporto predefinito per gestire gli input non validi. Componenti di convalida del modulo:
    • Consentire all'utente di specificare errori di input non validi e ricevere errori di convalida nell'oggetto associato EditContext.
    • Visualizzare gli errori di convalida nell'interfaccia utente senza interferire con l'utente immettendo dati webform aggiuntivi.

Stringhe di formato

Il data binding funziona con una singola DateTime stringa di formato usando @bind:format="{FORMAT STRING}", dove il segnaposto è la {FORMAT STRING} stringa di formato. Altre espressioni di formato, ad esempio i formati valuta o numero, non sono disponibili in questo momento, ma potrebbero essere aggiunte in una versione futura.

Pages/DateBinding.razor:

@page "/date-binding"

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

<p>
    <code>startDate</code>: @startDate
</p>

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

Nel codice precedente il <input> tipo di campo dell'elemento (type attributo) è predefinito su text.

Nullable System.DateTime e System.DateTimeOffset sono supportati:

private DateTime? date;
private DateTimeOffset? dateOffset;

Se si specifica un formato per il date tipo di campo, non è consigliabile perché Blazor è disponibile il supporto predefinito per formattare le date. Nonostante la raccomandazione, usare solo il formato data per l'associazione yyyy-MM-dd per funzionare correttamente se viene fornito un formato con il date tipo di campo:

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

Associazione a una proprietà con C# get e set funzioni di accesso

Le funzioni di accesso e set C# get possono essere usate per creare un comportamento di formato di associazione personalizzato, come illustrato nel componente seguenteDecimalBinding. Il componente associa un decimale positivo o negativo con un massimo di tre cifre decimali a un <input> elemento tramite una string proprietà (DecimalValue).

Pages/DecimalBinding.razor:

@page "/decimal-binding"
@using System.Globalization

<p>
    <label>
        Decimal value (±0.000 format):
        <input @bind="DecimalValue" />
    </label>
</p>

<p>
    <code>decimalValue</code>: @decimalValue
</p>

@code {
    private decimal decimalValue = 1.1M;
    private NumberStyles style = 
        NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
    private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");

    private string DecimalValue
    {
        get => decimalValue.ToString("0.000", culture);
        set
        {
            if (Decimal.TryParse(value, style, culture, out var number))
            {
                decimalValue = Math.Round(number, 3);
            }
        }
    }
}

Associazione con parametri del componente

Uno scenario comune associa una proprietà di un componente figlio a una proprietà nel relativo componente padre. Questo scenario viene chiamato associazione concatenata perché si verificano simultaneamente più livelli di associazione.

I parametri del componente consentono le proprietà di associazione di un componente padre con @bind-{PROPERTY} sintassi, in cui il {PROPERTY} segnaposto è la proprietà da associare.

Non è possibile implementare associazioni concatenati con @bind sintassi nel componente figlio. Un gestore eventi e un valore devono essere specificati separatamente per supportare l'aggiornamento della proprietà nel componente padre dal componente figlio.

Il componente padre sfrutta ancora la @bind sintassi per configurare l'associazione dei dati con il componente figlio.

Il componente seguente ChildBind include un Year parametro componente e un EventCallback<TValue>oggetto . Per convenzione, il EventCallback<TValue> parametro per il parametro deve essere denominato come nome del parametro del componente con un suffisso "Changed". La sintassi di denominazione è , dove il segnaposto è {PARAMETER NAME}Changedil {PARAMETER NAME} nome del parametro. Nell'esempio seguente viene EventCallback<TValue> denominato YearChanged.

EventCallback.InvokeAsync richiama il delegato associato all'associazione con l'argomento specificato e invia una notifica evento per la proprietà modificata.

Shared/ChildBind.razor:

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

@code {
    private Random r = new();

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

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

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

Per altre informazioni sugli eventi e EventCallback<TValue>, vedere la sezione EventCallback dell'articolo sulla gestione degli eventi ASP.NET CoreBlazor.

Nel componente seguente Parent1 il year campo è associato al Year parametro del componente figlio. Il Year parametro è associabile perché ha un evento complementare YearChanged che corrisponde al tipo del Year parametro.

Pages/Parent1.razor:

@page "/parent-1"

<h1>Parent Component</h1>

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

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

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

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

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

Per convenzione, una proprietà può essere associata a un gestore eventi corrispondente includendo un @bind-{PROPERTY}:event attributo assegnato al gestore, dove il {PROPERTY} segnaposto è la proprietà. <ChildBind @bind-Year="year" /> equivale alla scrittura:

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

In un esempio più sofisticato e reale, il componente seguente PasswordEntry :

  • Imposta il valore di un <input> elemento su un password campo.
  • Espone le modifiche di una proprietà a un Password componente padre con un EventCallback oggetto che passa il valore corrente del campo figlio password come argomento.
  • Usa l'evento per attivare il onclickToggleShowPassword metodo. Per altre informazioni, vedere gestione degli eventi ASP.NET CoreBlazor.

Shared/PasswordEntry.razor:

<div class="card bg-light mt-3" style="width:22rem ">
    <div class="card-body">
        <h3 class="card-title">Password Component</h3>
        <p class="card-text">
            <label>
                Password:
                <input @oninput="OnPasswordChanged"
                       required
                       type="@(showPassword ? "text" : "password")"
                       value="@password" />
            </label>
        </p>
        <button class="btn btn-primary" @onclick="ToggleShowPassword">
            Show password
        </button>
    </div>
</div>

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

Il PasswordEntry componente viene usato in un altro componente, ad esempio l'esempio di componente seguente PasswordBinding .

Pages/PasswordBinding.razor:

@page "/password-binding"

<h1>Password Binding</h1>

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

<p>
    <code>password</code>: @password
</p>

@code {
    private string password = "Not set";
}

Quando il componente viene inizialmente eseguito il PasswordBinding rendering, il password valore di Not set viene visualizzato nell'interfaccia utente. Dopo il rendering iniziale, il valore di password riflette le modifiche apportate al Password valore del parametro del componente nel PasswordEntry componente.

Nota

L'esempio precedente associa la password unidirezionale dal componente figlio PasswordEntry al componente padre PasswordBinding . L'associazione bidirezionale non è un requisito in questo scenario se l'obiettivo è che l'app disponga di un componente di immissione della password condivisa per il riutilizzo dell'app che passa semplicemente la password all'elemento padre. Per un approccio che consente l'associazione bidirezionale senza scrivere direttamente nel parametro del componente figlio, vedere l'esempioNestedChild del componente nella sezione Bind in più di due componenti di questo articolo.

Eseguire controlli o errori di trappola nel gestore. Il componente modificato PasswordEntry seguente fornisce commenti e suggerimenti immediati all'utente se viene usato uno spazio nel valore della password.

Shared/PasswordEntry.razor:

<div class="card bg-light mt-3" style="width:22rem ">
    <div class="card-body">
        <h3 class="card-title">Password Component</h3>
        <p class="card-text">
            <label>
                Password:
                <input @oninput="OnPasswordChanged"
                       required
                       type="@(showPassword ? "text" : "password")"
                       value="@password" />
            </label>
            <span class="text-danger">@validationMessage</span>
        </p>
        <button class="btn btn-primary" @onclick="ToggleShowPassword">
            Show password
        </button>
    </div>
</div>

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

Associare più di due componenti

È possibile associare i parametri tramite qualsiasi numero di componenti annidati, ma è necessario rispettare il flusso unidirezionale dei dati:

  • Le notifiche di modifica passano alla gerarchia.
  • I nuovi valori dei parametri passano verso il basso nella gerarchia.

Un approccio comune e consigliato consiste nell'archiviare solo i dati sottostanti nel componente padre per evitare qualsiasi confusione sullo stato da aggiornare, come illustrato nell'esempio seguente.

Pages/Parent2.razor:

@page "/parent-2"

<h1>Parent Component</h1>

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

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

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

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

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

Shared/NestedChild.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>

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

Avviso

In genere, evitare di creare componenti che scrivono direttamente nei propri parametri del componente. Il componente precedente NestedChild usa una BoundValue proprietà anziché scrivere direttamente nel parametro ChildMessage . Per altre informazioni, vedere componenti ASP.NET CoreRazor.

Shared/NestedGrandchild.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}");
    }
}

Per un approccio alternativo adatto alla condivisione dei dati in memoria e tra i componenti che non sono necessariamente annidati, vedere ASP.NET Core Blazor gestione dello stato.

Risorse aggiuntive

Razor i componenti forniscono funzionalità di data binding con l'attributo @bindRazor direttiva con un campo, una proprietà o Razor un valore di espressione.

L'esempio seguente associa:

  • Valore <input> dell'elemento nel campo C# inputValue .
  • Un secondo <input> valore dell'elemento nella proprietà C# InputValue .

Quando un <input> elemento perde lo stato attivo, viene aggiornato il campo o la proprietà associata.

Pages/Bind.razor:

@page "/bind"

<p>
    <input @bind="inputValue" />
</p>

<p>
    <input @bind="InputValue" />
</p>

<ul>
    <li><code>inputValue</code>: @inputValue</li>
    <li><code>InputValue</code>: @InputValue</li>
</ul>

@code {
    private string inputValue;

    private string InputValue { get; set; }
}

La casella di testo viene aggiornata nell'interfaccia utente solo quando il componente viene eseguito il rendering, non in risposta alla modifica del valore del campo o della proprietà. Poiché i componenti vengono visualizzati dopo l'esecuzione del codice del gestore eventi, gli aggiornamenti dei campi e delle proprietà vengono in genere riflessi nell'interfaccia utente immediatamente dopo l'attivazione di un gestore eventi.

Come dimostrazione del modo in cui il data binding si compone in HTML, nell'esempio seguente viene associata la InputValue proprietà agli attributi e onchange agli attributi del value secondo <input> elemento (change). Il secondo <input> elemento nell'esempio seguente è una dimostrazione di concetto e non è destinato a suggerire come associare i dati nei Razor componenti.

Pages/BindTheory.razor:

@page "/bind-theory"

<p>
    <label>
        Normal Blazor binding: 
        <input @bind="InputValue" />
    </label>
</p>

<p>
    <label>
        Demonstration of equivalent HTML binding: 
        <input value="@InputValue"
            @onchange="@((ChangeEventArgs __e) => InputValue = __e.Value.ToString())" />
    </label>
</p>

<p>
    <code>InputValue</code>: @InputValue
</p>

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

Quando viene eseguito il rendering del componente, l'elemento BindTheoryvalue dimostrativo <input> HTML proviene dalla InputValue proprietà . Quando l'utente immette un valore nella casella di testo e modifica lo stato attivo dell'elemento, l'evento onchange viene attivato e la InputValue proprietà viene impostata sul valore modificato. In realtà, l'esecuzione del codice è più complessa perché @bind gestisce i casi in cui vengono eseguite le conversioni dei tipi. In generale, @bind associa il valore corrente di un'espressione all'attributo dell'oggetto value<input> e gestisce le modifiche usando il gestore registrato.

Associare una proprietà o un campo ad altri eventi DOM (Document Object Model) includendo un attributo con un @bind:event="{EVENT}" evento DOM per il {EVENT} segnaposto. Nell'esempio seguente la InputValue proprietà viene associata al <input> valore dell'elemento oninput quando viene attivato l'evento dell'elemento (input). A differenza dell'evento (change), che viene generato quando l'elemento onchange perde lo stato attivo, oninput (input) viene generato quando il valore della casella di testo cambia.

Page/BindEvent.razor:

@page "/bind-event"

<p>
    <input @bind="InputValue" @bind:event="oninput" />
</p>

<p>
    <code>InputValue</code>: @InputValue
</p>

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

Razor l'associazione di attributi è distinzione tra maiuscole e minuscole:

  • @bind e @bind:event sono validi.
  • @Bind/@Bind:Event(lettere maiuscole e E) o@BIND:EVENT@BIND/(tutte le lettere B maiuscole) non sono valide.

Opzioni dell'elemento di associazione <select> ai valori degli oggetti null C#

Non è possibile rappresentare un <select> valore dell'opzione di elemento come valore dell'oggetto null C# perché:

  • Gli attributi HTML non possono avere null valori. L'equivalente più vicino a null in HTML è assenza dell'attributo HTML value dall'elemento <option> .
  • Quando si seleziona un oggetto <option> senza value attributo, il browser considera il valore come contenuto di testo di tale <option>elemento.

Il Blazor framework non tenta di eliminare il comportamento predefinito perché implica:

  • Creazione di una catena di soluzioni alternative speciali nel framework.
  • Modifiche di rilievo al comportamento corrente del framework.

Il Blazor framework non gestisce null automaticamente le conversioni di stringhe vuote durante il tentativo di associazione bidirezionale al valore di un'istanza <select>. Per altre informazioni, vedere Correzione dell'associazione <select> a un valore Null (dotnet/aspnetcore #23221).

Valori non riparabili

Quando un utente fornisce un valore non verificabile a un elemento in ingresso dati, il valore non verificabile viene automaticamente ripristinato al relativo valore precedente quando viene attivato l'evento di associazione.

Prendere in considerazione il componente seguente, in cui un <input> elemento è associato a un tipo con un int valore iniziale di 123.

Pages/UnparsableValues.razor:

@page "/unparseable-values"

<p>
    <input @bind="inputValue" />
</p>

<p>
    <code>inputValue</code>: @inputValue
</p>

@code {
    private int inputValue = 123;
}

Per impostazione predefinita, l'associazione si applica all'evento dell'elemento onchange . Se l'utente aggiorna il valore della voce della casella di testo in 123.45 e modifica lo stato attivo, il valore dell'elemento viene ripristinato 123 quando onchange viene generato. Quando il valore viene rifiutato a favore del valore 123.45 originale di 123, l'utente riconosce che il valore non è stato accettato.

Per l'evento oninput (@bind:event="oninput"), si verifica una riversione del valore dopo qualsiasi sequenza di tasti che introduce un valore non verificabile. Quando si punta all'evento oninput con un tipo associato, un utente non può digitare un intcarattere punto (.). Un carattere dot (.) viene rimosso immediatamente, quindi l'utente riceve un feedback immediato che solo i numeri interi sono consentiti. Esistono scenari in cui il ripristino del valore dell'evento oninput non è ideale, ad esempio quando l'utente deve essere autorizzato a cancellare un valore non verificabile <input> . Le alternative includono:

  • Non usare l'evento oninput . Usare l'evento predefinito onchange , in cui un valore non valido non viene ripristinato finché l'elemento non perde lo stato attivo.
  • Eseguire il binding a un tipo nullable, ad esempio int? o string e fornire l'associazione a una proprietà con logica personalizzata get e set di accesso per gestire voci non valide.
  • Usare un componente di convalida del modulo, ad InputNumber<TValue> esempio o InputDate<TValue>. I componenti di convalida dei moduli forniscono il supporto predefinito per gestire gli input non validi. Componenti di convalida del modulo:
    • Consentire all'utente di specificare errori di input non validi e ricevere errori di convalida nell'oggetto associato EditContext.
    • Visualizzare gli errori di convalida nell'interfaccia utente senza interferire con l'utente immettendo dati webform aggiuntivi.

Stringhe di formato

Il data binding funziona con una singola DateTime stringa di formato usando @bind:format="{FORMAT STRING}", dove il segnaposto è la {FORMAT STRING} stringa di formato. Altre espressioni di formato, ad esempio i formati valuta o numero, non sono disponibili in questo momento, ma potrebbero essere aggiunte in una versione futura.

Pages/DateBinding.razor:

@page "/date-binding"

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

<p>
    <code>startDate</code>: @startDate
</p>

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

Nel codice precedente il <input> tipo di campo dell'elemento (type attributo) è predefinito su text.

Nullable System.DateTime e System.DateTimeOffset sono supportati:

private DateTime? date;
private DateTimeOffset? dateOffset;

Se si specifica un formato per il date tipo di campo, non è consigliabile perché Blazor è disponibile il supporto predefinito per formattare le date. Nonostante la raccomandazione, usare solo il formato data per l'associazione yyyy-MM-dd per funzionare correttamente se viene fornito un formato con il date tipo di campo:

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

Associazione a una proprietà con C# get e set funzioni di accesso

Le funzioni di accesso e set C# get possono essere usate per creare un comportamento di formato di associazione personalizzato, come illustrato nel componente seguenteDecimalBinding. Il componente associa un decimale positivo o negativo con un massimo di tre cifre decimali a un <input> elemento tramite una string proprietà (DecimalValue).

Pages/DecimalBinding.razor:

@page "/decimal-binding"
@using System.Globalization

<p>
    <label>
        Decimal value (±0.000 format):
        <input @bind="DecimalValue" />
    </label>
</p>

<p>
    <code>decimalValue</code>: @decimalValue
</p>

@code {
    private decimal decimalValue = 1.1M;
    private NumberStyles style = 
        NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
    private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");

    private string DecimalValue
    {
        get => decimalValue.ToString("0.000", culture);
        set
        {
            if (Decimal.TryParse(value, style, culture, out var number))
            {
                decimalValue = Math.Round(number, 3);
            }
        }
    }
}

Associazione con parametri del componente

Uno scenario comune associa una proprietà di un componente figlio a una proprietà nel relativo componente padre. Questo scenario viene chiamato associazione concatenata perché si verificano simultaneamente più livelli di associazione.

I parametri del componente consentono le proprietà di associazione di un componente padre con @bind-{PROPERTY} sintassi, in cui il {PROPERTY} segnaposto è la proprietà da associare.

Non è possibile implementare associazioni concatenati con @bind sintassi nel componente figlio. Un gestore eventi e un valore devono essere specificati separatamente per supportare l'aggiornamento della proprietà nel componente padre dal componente figlio.

Il componente padre sfrutta ancora la @bind sintassi per configurare l'associazione dei dati con il componente figlio.

Il componente seguente ChildBind include un Year parametro componente e un EventCallback<TValue>oggetto . Per convenzione, il EventCallback<TValue> parametro per il parametro deve essere denominato come nome del parametro del componente con un suffisso "Changed". La sintassi di denominazione è , dove il segnaposto è {PARAMETER NAME}Changedil {PARAMETER NAME} nome del parametro. Nell'esempio seguente viene EventCallback<TValue> denominato YearChanged.

EventCallback.InvokeAsync richiama il delegato associato all'associazione con l'argomento specificato e invia una notifica evento per la proprietà modificata.

Shared/ChildBind.razor:

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

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

Per altre informazioni sugli eventi e EventCallback<TValue>, vedere la sezione EventCallback dell'articolo sulla gestione degli eventi ASP.NET CoreBlazor.

Nel componente seguente Parent1 il year campo è associato al Year parametro del componente figlio. Il Year parametro è associabile perché ha un evento complementare YearChanged che corrisponde al tipo del Year parametro.

Pages/Parent1.razor:

@page "/parent-1"

<h1>Parent Component</h1>

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

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

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

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

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

Per convenzione, una proprietà può essere associata a un gestore eventi corrispondente includendo un @bind-{PROPERTY}:event attributo assegnato al gestore, dove il {PROPERTY} segnaposto è la proprietà. <ChildBind @bind-Year="year" /> equivale alla scrittura:

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

In un esempio più sofisticato e reale, il componente seguente PasswordEntry :

  • Imposta il valore di un <input> elemento su un password campo.
  • Espone le modifiche di una proprietà a un Password componente padre con un EventCallback oggetto che passa il valore corrente del campo figlio password come argomento.
  • Usa l'evento per attivare il onclickToggleShowPassword metodo. Per altre informazioni, vedere gestione degli eventi ASP.NET CoreBlazor.

Shared/PasswordEntry.razor:

<div class="card bg-light mt-3" style="width:22rem ">
    <div class="card-body">
        <h3 class="card-title">Password Component</h3>
        <p class="card-text">
            <label>
                Password:
                <input @oninput="OnPasswordChanged"
                       required
                       type="@(showPassword ? "text" : "password")"
                       value="@password" />
            </label>
        </p>
        <button class="btn btn-primary" @onclick="ToggleShowPassword">
            Show password
        </button>
    </div>
</div>

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

Il PasswordEntry componente viene usato in un altro componente, ad esempio l'esempio di componente seguente PasswordBinding .

Pages/PasswordBinding.razor:

@page "/password-binding"

<h1>Password Binding</h1>

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

<p>
    <code>password</code>: @password
</p>

@code {
    private string password = "Not set";
}

Quando il componente viene inizialmente eseguito il PasswordBinding rendering, il password valore di Not set viene visualizzato nell'interfaccia utente. Dopo il rendering iniziale, il valore di password riflette le modifiche apportate al Password valore del parametro del componente nel PasswordEntry componente.

Nota

L'esempio precedente associa la password unidirezionale dal componente figlio PasswordEntry al componente padre PasswordBinding . L'associazione bidirezionale non è un requisito in questo scenario se l'obiettivo è che l'app disponga di un componente di immissione della password condivisa per il riutilizzo dell'app che passa semplicemente la password all'elemento padre. Per un approccio che consente l'associazione bidirezionale senza scrivere direttamente nel parametro del componente figlio, vedere l'esempioNestedChild del componente nella sezione Bind in più di due componenti di questo articolo.

Eseguire controlli o errori di trappola nel gestore. Il componente modificato PasswordEntry seguente fornisce commenti e suggerimenti immediati all'utente se viene usato uno spazio nel valore della password.

Shared/PasswordEntry.razor:

<div class="card bg-light mt-3" style="width:22rem ">
    <div class="card-body">
        <h3 class="card-title">Password Component</h3>
        <p class="card-text">
            <label>
                Password:
                <input @oninput="OnPasswordChanged"
                       required
                       type="@(showPassword ? "text" : "password")"
                       value="@password" />
            </label>
            <span class="text-danger">@validationMessage</span>
        </p>
        <button class="btn btn-primary" @onclick="ToggleShowPassword">
            Show password
        </button>
    </div>
</div>

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

Associare più di due componenti

È possibile associare i parametri tramite qualsiasi numero di componenti annidati, ma è necessario rispettare il flusso unidirezionale dei dati:

  • Le notifiche di modifica passano alla gerarchia.
  • I nuovi valori dei parametri passano verso il basso nella gerarchia.

Un approccio comune e consigliato consiste nell'archiviare solo i dati sottostanti nel componente padre per evitare qualsiasi confusione sullo stato da aggiornare, come illustrato nell'esempio seguente.

Pages/Parent2.razor:

@page "/parent-2"

<h1>Parent Component</h1>

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

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

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

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

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

Shared/NestedChild.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>

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

Avviso

In genere, evitare di creare componenti che scrivono direttamente nei propri parametri del componente. Il componente precedente NestedChild usa una BoundValue proprietà anziché scrivere direttamente nel parametro ChildMessage . Per altre informazioni, vedere componenti ASP.NET CoreRazor.

Shared/NestedGrandchild.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}");
    }
}

Per un approccio alternativo adatto alla condivisione dei dati in memoria e tra i componenti che non sono necessariamente annidati, vedere ASP.NET Core Blazor gestione dello stato.

Risorse aggiuntive