BlazorASP.NET Core datová vazba

Razor Komponenty poskytují funkce datových vazeb s @bind Razor atributem direktivy s polem, vlastností nebo hodnotou Razor výrazu.

Následující příklad vytvoří vazbu:

  • Hodnota <input> prvku pole jazyka inputValue C#.
  • Druhá hodnota <input> prvku vlastnosti jazyka InputValue C#.

Když prvek <input> ztratí fokus, aktualizuje se jeho vázané pole nebo vlastnost.

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

Textové pole se v uživatelském rozhraní aktualizuje jenom při vykreslení komponenty, nikoli v reakci na změnu hodnoty pole nebo vlastnosti. Vzhledem k tomu, že se komponenty vykreslují po spuštění kódu obslužné rutiny události, změny polí a vlastností se obvykle projeví v uživatelském rozhraní ihned po aktivaci obslužné rutiny události.

Jako ukázku toho, jak se datová vazba sčítá v HTML, následující příklad naváže vlastnost na atributy InputValue a <input> druhého value onchange prvku. Druhým prvkem v následujícím příkladu je ukázka konceptu, která nemá navrhovat způsob vazby <input> dat v Razor komponentách.

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

Při vykreslení komponenty pochází element ukázky BindTheory HTML z vlastnosti value <input> InputValue . Když uživatel zadá hodnotu do textového pole a změní fokus prvku, událost se aktivuje a vlastnost se nastaví onchange InputValue na změněnou hodnotu. Ve skutečnosti je spouštění kódu složitější, protože @bind zpracovává případy, kdy se provádí převody typů. Obecně platí, že aktuální hodnotu výrazu přidruží k atributu a zpracovává @bind změny pomocí registrované obslužné value rutiny.

Vytvořte vazbu vlastnosti nebo pole u jiných model DOM (Document Object Model) (DOM) zahrnutím atributu s @bind:event="{EVENT}" událostí modelu DOM pro zástupný {EVENT} symbol. Následující příklad vytvoří vazbu vlastnosti na hodnotu prvku InputValue <input> při aktivaci oninput události prvku. Na rozdíl onchange od události, která se vyžádá, když prvek ztratí fokus, se při změně hodnoty oninput textového pole vyhodí.

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 V vazbě atributů se rozlišují velká a malá písmena:

  • @bind``@bind:eventa jsou platné.
  • @Bind/@Bind:Event(velká písmena B E a ) nebo @BIND / @BIND:EVENT (všechna velká písmena) jsou neplatná.

Výběr více možností <input> s prvky

Vazba podporuje multiple výběr možností s <input> elementy. Událost @onchange poskytuje pole vybraných prvků prostřednictvím argumentů událostí ( ChangeEventArgs ). Hodnota musí být svázaná s typem pole.

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

    void SelectedCarsChanged(ChangeEventArgs e)
    {
        SelectedCars = (string[])e.Value;
    }
}

Informace o tom, jak se zpracovávají prázdné řetězce a hodnoty v datové vazbě, najdete v části Možnosti elementu Vazby na null hodnoty objektů <select> null jazyka C#.

Možnosti <select> elementu vazby na hodnoty objektů jazyka null C#

Neexistuje žádný rozumný způsob, jak reprezentovat hodnotu možnosti prvku jako hodnotu objektu <select> jazyka null C#, protože:

  • Atributy HTML nemůže mít null hodnoty. Nejbližší ekvivalent v null HTML je absence atributu HTML v value <option> elementu .
  • Při výběru objektu bez atributu prohlížeč považuje hodnotu za <option> value textový obsah tohoto <option> elementu .

Rozhraní Blazor se nepokusí potlačit výchozí chování, protože by zahrnovalo:

  • Vytvoření řetězce alternativních řešení pro zvláštní případy v rámci architektury
  • Rozbíjení změn chování aktuální architektury

Nejužitelnějším null ekvivalentem v HTML je prázdný řetězec value . Rozhraní Blazor zpracovává null převody prázdných řetězců pro dvousestavovou vazbu <select> na hodnotu typu .

Nepochyitelné hodnoty

Když uživatel poskytne neopravitelnou hodnotu prvku vázaného na data, neopravitelná hodnota se při aktivaci události vazby automaticky vrátí na předchozí hodnotu.

Vezměte v úvahu následující komponentu, kde je prvek svázán s <input> int typem s počáteční hodnotou 123 .

Pages/UnparsableValues.razor:

@page "/unparseable-values"

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

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

@code {
    private int inputValue = 123;
}

Vazba se ve výchozím nastavení vztahuje na událost onchange elementu. Pokud uživatel aktualizuje hodnotu položky textového pole na a změní fokus, hodnota prvku se při spuštění 123.45 123 vrátí na onchange . Když se hodnota zamítne ve prospěch původní hodnoty , uživatel pochopí, že jeho 123.45 123 hodnota nebyla přijata.

V případě události ( ) dojde k převrácení hodnoty po stisknutí klávesy, která zavádí oninput @bind:event="oninput" neopravitelnou hodnotu. Při cílení na událost s typem svázaným s objektem je uživateli znemožněn zadávání oninput int tečky ( . ). Tečka ( ) je okamžitě odebrána, takže uživatel obdrží okamžitou zpětnou vazbu, že . jsou povolena pouze celá čísla. Existují scénáře, kdy vrácení hodnoty události není ideální, například kdy by uživatel měl mít povoleno vymazat neopravitelnou oninput <input> hodnotu. Mezi alternativy patří:

  • Nepoužívejte oninput událost . Použijte výchozí událost, kde se neplatná hodnota nevrátila, onchange dokud prvek neztratí fokus.
  • Vytvořte vazbu na typ s možnou hodnotou null, například nebo , a zadejte vlastní logiku a logiku přístupového objektu pro int? string zpracování neplatných get set položek.
  • Použijte komponentu ověřování formuláře, například InputNumber<TValue> nebo InputDate<TValue> . Součásti ověřování formulářů poskytují integrovanou podporu pro správu neplatných vstupů. Součásti ověřování formulářů:
    • Povolte uživateli zadání neplatného vstupu a příjem chyb ověření v přidruženém souboru EditContext .
    • Zobrazení chyb ověřování v uživatelském rozhraní bez toho, aby uživatel zasahoval do zadávání dalších dat webového formuláře

Formátování řetězců

Datová vazba funguje s jedním DateTime formátovacím řetězcem pomocí @bind:format="{FORMAT STRING}" , kde zástupný symbol je {FORMAT STRING} formátovací řetězec. Jiné výrazy formátu, jako jsou formáty měn nebo čísel, nejsou v tuto chvíli dostupné, ale můžou být přidány v budoucí verzi.

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

V předchozím kódu má typ pole elementu <input> type (atribut) výchozí hodnotu text .

S System.DateTime povolenou hodnotou null System.DateTimeOffset a jsou podporovány:

private DateTime? date;
private DateTimeOffset? dateOffset;

Zadání formátu pro typ pole se nedoporučuje, protože má integrovanou podporu date Blazor formátování kalendářních dat. I přes doporučení používejte formát data pro vazbu, aby správně fungovala, pouze pokud je dodaný formát yyyy-MM-dd s date typem pole:

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

Vlastní formáty vazeb

Jazyk C# a get set přístupové objekty lze použít k vytvoření vlastního chování formátu vazby, jak ukazuje následující DecimalBinding komponenta. Komponenta vytvoří vazbu kladného nebo záporného desetinného čísla až se třemi desetinnými místy na <input> prvek pomocí vlastnosti ( string DecimalValue ).

Pages/DecimalBinding.razor:

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

<p>
    <label>
        Decimal value (&plusmn;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);
            }
        }
    }
}

Vytvoření vazby s parametry komponenty

Běžným scénářem je vazba vlastnosti podřízené komponenty na vlastnost v její nadřazené komponentě. Tento scénář se nazývá zřetězovaná vazba, protože současně dochází k více úrovním vazby.

Parametry komponenty povolují vlastnosti vazby nadřazené komponenty se @bind-{PROPERTY} syntaxí, kde zástupný symbol je {PROPERTY} vlastnost, která se má svázat.

V podřízené komponentě nelze implementovat zřetězované vazby @bind se syntaxí. Obslužná rutina události a hodnota musí být zadány samostatně pro podporu aktualizace vlastnosti v nadřazené z podřízené komponenty.

Nadřazená komponenta stále využívá syntaxi k nastavení datové @bind vazby s podřízeným komponentou.

Následující ChildBind komponenta má parametr komponenty a Year EventCallback<TValue> . Podle konvence musí být parametr pro pojmenován jako název parametru komponenty EventCallback<TValue> s Changed příponou " ". Syntaxe pojmenování je {PARAMETER NAME}Changed , kde zástupný symbol je název {PARAMETER NAME} parametru. V následujícím příkladu má EventCallback<TValue> název YearChanged .

EventCallback.InvokeAsync vyvolá delegáta přidruženého k vazbě se poskytnutým argumentem a odešle oznámení události pro změněnou vlastnost.

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

Další informace o událostech EventCallback<TValue> a najdete v části EventCallback tohoto ASP.NET Core Blazor zpracování událostí článku.

V následující Parent komponentě year je pole vázané na parametr podřízené Year komponenty. Parametr Year je možné svázat, protože má doprovodnou YearChanged událost, která odpovídá typu Year parametru.

Pages/Parent.razor:

@page "/Parent"

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

Podle konvence může být vlastnost svázaná s odpovídající obslužnou rutinou události zahrnutím atributu přiřazeného obslužné rutině, kde zástupný symbol @bind-{PROPERTY}:event {PROPERTY} je vlastnost . <ChildBind @bind-Year="year" /> je ekvivalentem zápisu:

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

V sofistikovanějším příkladu z reálného světa se používá následující PasswordEntry komponenta:

  • Nastaví <input> hodnotu prvku na password pole.
  • Zpřístupňuje změny vlastnosti nadřazené komponentě pomocí objektu , který předává aktuální hodnotu podřízeného pole Password EventCallback jako svůj password argument.
  • Použije onclick událost k aktivaci ToggleShowPassword metody . Další informace naleznete v tématu ASP.NET Core Blazor zpracování událostí.

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

Komponenta PasswordEntry se používá v jiné komponentě, například v následujícím PasswordBinding příkladu komponenty.

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

Při PasswordBinding počátečním vykreslení komponenty se v uživatelském rozhraní password zobrazí hodnota Not set . Po počátečním vykreslení hodnota odráží změny provedené v hodnotě parametru komponenty password Password v PasswordEntry komponentě.

Poznámka

Předchozí příklad vytvoří vazbu hesla jedním směrem z podřízené komponenty na PasswordEntry nadřazenou PasswordBinding komponentu. Dvousměnná vazba není v tomto scénáři požadavkem, pokud je cílem aplikace mít sdílenou komponentu pro zadávání hesel, aby ji bylo možné opakovaně používat v aplikaci, která pouze předává heslo nadřazenému serveru. Přístup, který umožňuje dvoucestné vazby bez přímého zápisu do parametru podřízené komponenty ,najdete v příkladu komponenty v části Vázání napříč více než dvěma součástmi NestedChild tohoto článku.

Provádění kontrol nebo zahodování chyb v obslužné rutině Následující revidovaná komponenta poskytuje uživateli okamžitou zpětnou vazbu, pokud se v hodnotě PasswordEntry hesla používá mezera.

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

Vytvoření vazby mezi více než dvěma komponentami

Parametry můžete svázat s libovolným počtem vnořených komponent, ale musíte respektovat jednosměnné toky dat:

  • Oznámení o změně se chytou nahoru v hierarchii.
  • Nové hodnoty parametrů prochádí hierarchii .

Běžným a doporučeným přístupem je ukládat pouze podkladová data v nadřazené komponentě, aby nedocházelo k nejasnostem o tom, jaký stav je třeba aktualizovat, jak je znázorněno v následujícím příkladu.

Pages/Parent.razor:

@page "/parent"

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

Upozornění

Obecně se vyhněte vytváření komponent, které zapisují přímo do vlastních parametrů součásti. Předchozí komponenta místo přímého zápisu do svého parametru používá NestedChild BoundValue vlastnost ChildMessage . Další informace naleznete v tématu RazorASP.NET Core Součásti.

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

Alternativní přístup vhodný ke sdílení dat v paměti a mezi komponentami, které nejsou nutně vnořené, najdete v tématu ASP.NET Core Blazor Správa stavu .

Další zdroje informací

Razor Komponenty poskytují funkce datových vazeb s @bind Razor atributem direktivy s polem, vlastností nebo hodnotou Razor výrazu.

Následující příklad vytvoří vazbu:

  • Hodnota <input> prvku pole jazyka inputValue C#.
  • Druhá hodnota <input> prvku vlastnosti jazyka InputValue C#.

Když prvek <input> ztratí fokus, aktualizuje se jeho vázané pole nebo vlastnost.

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

Textové pole se v uživatelském rozhraní aktualizuje jenom při vykreslení komponenty, nikoli v reakci na změnu hodnoty pole nebo vlastnosti. Vzhledem k tomu, že se komponenty vykreslují po spuštění kódu obslužné rutiny události, změny polí a vlastností se obvykle projeví v uživatelském rozhraní ihned po aktivaci obslužné rutiny události.

Jako ukázku toho, jak se datová vazba sčítá v HTML, následující příklad naváže vlastnost na atributy InputValue a <input> druhého value onchange prvku. Druhým prvkem v následujícím příkladu je ukázka konceptu, která nemá navrhovat způsob vazby <input> dat v Razor komponentách.

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

Při vykreslení komponenty pochází element ukázky BindTheory HTML z vlastnosti value <input> InputValue . Když uživatel zadá hodnotu do textového pole a změní fokus prvku, událost se aktivuje a vlastnost se nastaví onchange InputValue na změněnou hodnotu. Ve skutečnosti je spouštění kódu složitější, protože @bind zpracovává případy, kdy se provádí převody typů. Obecně platí, že aktuální hodnotu výrazu přidruží k atributu a zpracovává @bind změny pomocí registrované obslužné value rutiny.

Vytvořte vazbu vlastnosti nebo pole u jiných model DOM (Document Object Model) (DOM) zahrnutím atributu s @bind:event="{EVENT}" událostí modelu DOM pro zástupný {EVENT} symbol. Následující příklad vytvoří vazbu vlastnosti na hodnotu prvku InputValue <input> při aktivaci oninput události prvku. Na rozdíl onchange od události, která se vyžádá, když prvek ztratí fokus, se při změně hodnoty oninput textového pole vyhodí.

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 V vazbě atributů se rozlišují velká a malá písmena:

  • @bind``@bind:eventa jsou platné.
  • @Bind/@Bind:Event(velká písmena B E a ) nebo @BIND / @BIND:EVENT (všechna velká písmena) jsou neplatná.

Možnosti <select> elementu vazby na hodnoty objektů jazyka null C#

Neexistuje žádný rozumný způsob, jak reprezentovat hodnotu možnosti prvku jako hodnotu objektu <select> jazyka null C#, protože:

  • Atributy HTML nemůže mít null hodnoty. Nejbližší ekvivalent v null HTML je absence atributu HTML v value <option> elementu .
  • Při výběru objektu bez atributu prohlížeč považuje hodnotu za <option> value textový obsah tohoto <option> elementu .

Rozhraní Blazor se nepokusí potlačit výchozí chování, protože by zahrnovalo:

  • Vytvoření řetězce alternativních řešení pro zvláštní případy v rámci architektury
  • Rozbíjení změn chování aktuální architektury

Nejužitelnějším null ekvivalentem v HTML je prázdný řetězec value . Rozhraní Blazor zpracovává null převody prázdných řetězců pro dvousestavovou vazbu <select> na hodnotu typu .

Nepochyitelné hodnoty

Když uživatel poskytne neopravitelnou hodnotu prvku vázaného na data, neopravitelná hodnota se při aktivaci události vazby automaticky vrátí na předchozí hodnotu.

Vezměte v úvahu následující komponentu, kde je prvek svázán s <input> int typem s počáteční hodnotou 123 .

Pages/UnparsableValues.razor:

@page "/unparseable-values"

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

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

@code {
    private int inputValue = 123;
}

Vazba se ve výchozím nastavení vztahuje na událost onchange elementu. Pokud uživatel aktualizuje hodnotu položky textového pole na a změní fokus, hodnota prvku se při spuštění 123.45 123 vrátí na onchange . Když se hodnota zamítne ve prospěch původní hodnoty , uživatel pochopí, že jeho 123.45 123 hodnota nebyla přijata.

V případě události ( ) dojde k převrácení hodnoty po stisknutí klávesy, která zavádí oninput @bind:event="oninput" neopravitelnou hodnotu. Při cílení na událost s typem svázaným s objektem je uživateli znemožněn zadávání oninput int tečky ( . ). Tečka ( ) je okamžitě odebrána, takže uživatel obdrží okamžitou zpětnou vazbu, že . jsou povolena pouze celá čísla. Existují scénáře, kdy vrácení hodnoty události není ideální, například kdy by uživatel měl mít povoleno vymazat neopravitelnou oninput <input> hodnotu. Mezi alternativy patří:

  • Nepoužívejte oninput událost . Použijte výchozí událost, kde se neplatná hodnota nevrátila, onchange dokud prvek neztratí fokus.
  • Vytvořte vazbu na typ s možnou hodnotou null, například nebo , a zadejte vlastní logiku a přístupový objekt pro int? string zpracování neplatných get set položek.
  • Použijte komponentu ověřování formuláře, například InputNumber<TValue> nebo InputDate<TValue> . Součásti ověřování formulářů poskytují integrovanou podporu pro správu neplatných vstupů. Součásti ověřování formulářů:
    • Povolte uživateli zadání neplatného vstupu a příjem chyb ověření v přidruženém souboru EditContext .
    • Zobrazovat chyby ověřování v uživatelském rozhraní bez toho, aby uživatel zasahoval do zadávání dalších dat webového formuláře.

Formátování řetězců

Datová vazba funguje s jedním DateTime formátovacím řetězcem pomocí @bind:format="{FORMAT STRING}" , kde zástupný symbol je {FORMAT STRING} formátovací řetězec. Jiné výrazy formátu, jako jsou formáty měn nebo čísel, nejsou v tuto chvíli dostupné, ale můžou být přidány v budoucí verzi.

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

V předchozím kódu je typ pole elementu <input> ( type atribut) ve výchozím nastavení text .

S System.DateTime povolenou hodnotou null System.DateTimeOffset a jsou podporovány:

private DateTime? date;
private DateTimeOffset? dateOffset;

Zadání formátu pro typ pole se nedoporučuje, protože má integrovanou podporu date Blazor formátování kalendářních dat. I přes doporučení používejte formát data pro vazbu, aby správně fungovala, pouze pokud je dodaný formát yyyy-MM-dd s date typem pole:

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

Vlastní formáty vazeb

Jazyk C# a get set přístupové objekty lze použít k vytvoření vlastního chování formátu vazby, jak ukazuje následující DecimalBinding komponenta. Komponenta vytvoří vazbu kladného nebo záporného desetinného čísla až se třemi desetinnými místy na <input> prvek pomocí vlastnosti ( string DecimalValue ).

Pages/DecimalBinding.razor:

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

<p>
    <label>
        Decimal value (&plusmn;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);
            }
        }
    }
}

Vazba s parametry komponenty

Běžným scénářem je vazba vlastnosti podřízené komponenty na vlastnost v její nadřazené komponentě. Tento scénář se nazývá zřetězovaná vazba, protože současně dochází k více úrovním vazby.

Parametry komponenty povolují vlastnosti vazby nadřazené komponenty se @bind-{PROPERTY} syntaxí, kde zástupný symbol je {PROPERTY} vlastnost, která se má svázat.

V podřízené komponentě nelze implementovat zřetězované vazby @bind se syntaxí. Obslužná rutina události a hodnota musí být zadány samostatně pro podporu aktualizace vlastnosti v nadřazené z podřízené komponenty.

Nadřazená komponenta stále využívá syntaxi k nastavení datové @bind vazby s podřízeným komponentou.

Následující ChildBind komponenta má parametr komponenty a Year EventCallback<TValue> . Podle konvence musí být parametr pro pojmenován jako název parametru komponenty EventCallback<TValue> s Changed příponou " ". Syntaxe pojmenování je {PARAMETER NAME}Changed , kde zástupný symbol je název {PARAMETER NAME} parametru. V následujícím příkladu má EventCallback<TValue> název YearChanged .

EventCallback.InvokeAsync vyvolá delegáta přidruženého k vazbě se poskytnutým argumentem a odešle oznámení události pro změněnou vlastnost.

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

Další informace o událostech EventCallback<TValue> a najdete v části EventCallback tohoto ASP.NET Core Blazor zpracování událostí článku.

V následující Parent komponentě year je pole vázané na parametr podřízené Year komponenty. Parametr Year je možné svázat, protože má doprovodnou YearChanged událost, která odpovídá typu Year parametru.

Pages/Parent.razor:

@page "/Parent"

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

Podle konvence může být vlastnost svázaná s odpovídající obslužnou rutinou události zahrnutím atributu přiřazeného obslužné rutině, kde zástupný symbol @bind-{PROPERTY}:event {PROPERTY} je vlastnost . <ChildBind @bind-Year="year" /> je ekvivalentem zápisu:

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

V sofistikovanějším příkladu z reálného světa se používá následující PasswordEntry komponenta:

  • Nastaví <input> hodnotu prvku na password pole.
  • Zpřístupňuje změny vlastnosti nadřazené komponentě pomocí objektu , který předává aktuální hodnotu podřízeného pole Password EventCallback jako svůj password argument.
  • Použije onclick událost k aktivaci ToggleShowPassword metody . Další informace naleznete v tématu ASP.NET Core Blazor zpracování událostí.

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

Komponenta PasswordEntry se používá v jiné komponentě, například v následujícím PasswordBinding příkladu komponenty.

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

Při PasswordBinding počátečním vykreslení komponenty se v uživatelském rozhraní password zobrazí hodnota Not set . Po počátečním vykreslení hodnota odráží změny provedené v hodnotě parametru komponenty password Password v PasswordEntry komponentě.

Poznámka

Předchozí příklad vytvoří vazbu hesla jedním směrem z podřízené komponenty na PasswordEntry nadřazenou PasswordBinding komponentu. Dvousměnná vazba není v tomto scénáři požadavkem, pokud je cílem aplikace mít sdílenou komponentu pro zadávání hesel, aby ji bylo možné opakovaně používat v aplikaci, která jenom předává heslo nadřazenému serveru. Přístup, který umožňuje dvoucestné vazby bez přímého zápisu do parametru podřízené komponenty ,najdete v příkladu komponenty v části Vázání napříč více než dvěma součástmi NestedChild tohoto článku.

Provádění kontrol nebo zachytání chyb v obslužné rutině Následující revidovaná komponenta poskytuje uživateli okamžitou zpětnou vazbu, pokud se v hodnotě PasswordEntry hesla používá mezera.

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

Vytvoření vazby mezi více než dvěma komponentami

Parametry můžete svázat s libovolným počtem vnořených komponent, ale musíte respektovat jednosměnné toky dat:

  • Oznámení o změně se chytou nahoru v hierarchii.
  • Nové hodnoty parametrů prochádí hierarchii .

Běžným a doporučeným přístupem je ukládat pouze podkladová data v nadřazené komponentě, aby nedocházelo k nejasnostem o tom, jaký stav je třeba aktualizovat, jak je znázorněno v následujícím příkladu.

Pages/Parent.razor:

@page "/parent"

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

Upozornění

Obecně se vyhněte vytváření komponent, které zapisují přímo do vlastních parametrů součásti. Předchozí komponenta místo přímého zápisu do svého parametru používá NestedChild BoundValue vlastnost ChildMessage . Další informace naleznete v tématu RazorASP.NET Core Součásti.

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

Alternativní přístup vhodný ke sdílení dat v paměti a mezi komponentami, které nejsou nutně vnořené, najdete v tématu ASP.NET Core Blazor Správa stavu .

Další zdroje informací

Razor Komponenty poskytují funkce datových vazeb s @bind Razor atributem direktivy s polem, vlastností nebo hodnotou Razor výrazu.

Následující příklad vytvoří vazbu:

  • Hodnota <input> prvku pole jazyka inputValue C#.
  • Druhá hodnota <input> prvku vlastnosti jazyka InputValue C#.

Když prvek <input> ztratí fokus, aktualizuje se jeho vázané pole nebo vlastnost.

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

Textové pole se v uživatelském rozhraní aktualizuje jenom při vykreslení komponenty, nikoli v reakci na změnu hodnoty pole nebo vlastnosti. Vzhledem k tomu, že se komponenty vykreslují po spuštění kódu obslužné rutiny události, změny polí a vlastností se obvykle projeví v uživatelském rozhraní ihned po aktivaci obslužné rutiny události.

Jako ukázku toho, jak se datová vazba sčítá v HTML, následující příklad naváže vlastnost na atributy InputValue a <input> druhého value onchange prvku. Druhým prvkem v následujícím příkladu je ukázka konceptu, která nemá navrhovat způsob vazby <input> dat v Razor komponentách.

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

Při vykreslení komponenty pochází element ukázky BindTheory HTML z vlastnosti value <input> InputValue . Když uživatel zadá hodnotu do textového pole a změní fokus prvku, událost se aktivuje a vlastnost se nastaví onchange InputValue na změněnou hodnotu. Ve skutečnosti je spouštění kódu složitější, protože @bind zpracovává případy, kdy se provádí převody typů. Obecně se aktuální hodnota výrazu přidruží k atributu a zpracovává @bind změny pomocí registrované obslužné value rutiny.

Vytvořte vazbu vlastnosti nebo pole u jiných model DOM (Document Object Model) (DOM) zahrnutím atributu s @bind:event="{EVENT}" událostí modelu DOM pro zástupný {EVENT} symbol. Následující příklad vytvoří vazbu vlastnosti na hodnotu prvku InputValue <input> při aktivaci oninput události prvku. Na rozdíl onchange od události, která se vyžádá, když prvek ztratí fokus, se při změně hodnoty oninput textového pole vyhodí.

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 V vazbě atributů se rozlišují velká a malá písmena:

  • @bind``@bind:eventa jsou platné.
  • @Bind/@Bind:Event(velká písmena B E a ) nebo @BIND / @BIND:EVENT (všechna velká písmena) jsou neplatná.

Možnosti <select> elementu vazby na hodnoty objektů jazyka null C#

Neexistuje žádný rozumný způsob, jak reprezentovat hodnotu možnosti prvku <select> jako hodnotu objektu jazyka null C#, protože:

  • Atributy HTML nemůže mít null hodnoty. Nejbližší ekvivalent v null HTML je absence atributu HTML v value <option> elementu .
  • Při výběru objektu bez atributu prohlížeč považuje hodnotu za <option> value textový obsah tohoto <option> elementu .

Rozhraní Blazor se nepokusí potlačit výchozí chování, protože by zahrnovalo:

  • Vytvoření řetězce alternativních řešení pro zvláštní případy v rámci architektury
  • Rozbíjení změn chování aktuální architektury

Při pokusu o dvousečtovou vazbu na hodnotu nezovládá rozhraní automaticky převody Blazor null <select> prázdných řetězců. Další informace najdete v tématu Oprava vazby na hodnotu null <select> (dotnet/aspnetcore #23221).

Nepochyitelné hodnoty

Když uživatel poskytne neopravitelnou hodnotu prvku vázaného na data, neopravitelná hodnota se při aktivaci události vazby automaticky vrátí na předchozí hodnotu.

Vezměte v úvahu následující komponentu, kde je prvek svázán s <input> int typem s počáteční hodnotou 123 .

Pages/UnparsableValues.razor:

@page "/unparseable-values"

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

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

@code {
    private int inputValue = 123;
}

Vazba se ve výchozím nastavení vztahuje na událost onchange elementu. Pokud uživatel aktualizuje hodnotu položky textového pole na a změní fokus, hodnota elementu se vrátí, když se 123.45 123 onchange vyhodí. Když se hodnota zamítne ve prospěch původní hodnoty , uživatel pochopí, že jeho 123.45 123 hodnota nebyla přijata.

V případě události ( ) dojde k převrácení hodnoty po stisknutí klávesy, která zavádí oninput @bind:event="oninput" neopravitelnou hodnotu. Při cílení na událost s typem svázaným s objektem je uživateli znemožněn zadávání oninput int tečky ( . ). Tečka ( ) je okamžitě odebrána, takže uživatel obdrží okamžitou zpětnou vazbu, že . jsou povolena pouze celá čísla. Existují scénáře, kdy vrácení hodnoty události není ideální, například kdy by uživatel měl mít povoleno vymazat neopravitelnou oninput <input> hodnotu. Mezi alternativy patří:

  • Nepoužívejte oninput událost . Použijte výchozí událost, kde se neplatná hodnota nevrátila, onchange dokud prvek neztratí fokus.
  • Vytvořte vazbu na typ s možnou hodnotou null, například nebo , a zadejte vlastní logiku a přístupový objekt pro int? string zpracování neplatných get set položek.
  • Použijte komponentu ověřování formuláře, například InputNumber<TValue> nebo InputDate<TValue> . Součásti ověřování formulářů poskytují integrovanou podporu pro správu neplatných vstupů. Součásti ověřování formulářů:
    • Povolte uživateli zadání neplatného vstupu a příjem chyb ověření v přidruženém souboru EditContext .
    • Zobrazovat chyby ověřování v uživatelském rozhraní bez toho, aby uživatel zasahoval do zadávání dalších dat webového formuláře.

Formátování řetězců

Datová vazba funguje s jedním DateTime formátovacím řetězcem pomocí @bind:format="{FORMAT STRING}" , kde zástupný symbol je {FORMAT STRING} formátovací řetězec. Jiné výrazy formátu, jako jsou formáty měn nebo čísel, nejsou v tuto chvíli dostupné, ale můžou být přidány v budoucí verzi.

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

V předchozím kódu je typ pole elementu <input> ( type atribut) ve výchozím nastavení text .

Podporují se System.DateTime hodnoty System.DateTimeOffset Nullable a :

private DateTime? date;
private DateTimeOffset? dateOffset;

Zadání formátu pro typ pole se nedoporučuje, protože má integrovanou podporu date Blazor formátování kalendářních dat. I přes doporučení používejte formát data pro vazbu, aby správně fungovala, pouze pokud je dodaný formát yyyy-MM-dd s date typem pole:

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

Vlastní formáty vazeb

Jazyk C# a get set přístupové objekty lze použít k vytvoření vlastního chování formátu vazby, jak ukazuje následující DecimalBinding komponenta. Komponenta vytvoří vazbu kladného nebo záporného desetinného čísla až se třemi desetinnými místy na <input> prvek pomocí vlastnosti ( string DecimalValue ).

Pages/DecimalBinding.razor:

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

<p>
    <label>
        Decimal value (&plusmn;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);
            }
        }
    }
}

Vazba s parametry komponenty

Běžným scénářem je vazba vlastnosti podřízené komponenty na vlastnost v její nadřazené komponentě. Tento scénář se nazývá zřetězovaná vazba, protože současně dochází k více úrovním vazby.

Parametry komponenty povolují vlastnosti vazby nadřazené komponenty se @bind-{PROPERTY} syntaxí, kde zástupný symbol je {PROPERTY} vlastnost, která se má svázat.

V podřízené komponentě nelze implementovat zřetězované vazby @bind se syntaxí. Obslužná rutina události a hodnota musí být zadány samostatně pro podporu aktualizace vlastnosti v nadřazené z podřízené komponenty.

Nadřazená komponenta stále využívá syntaxi k nastavení datové @bind vazby s podřízeným komponentou.

Následující ChildBind komponenta má parametr komponenty a Year EventCallback<TValue> . Podle konvence musí být parametr pro pojmenován jako název parametru komponenty EventCallback<TValue> s Changed příponou " ". Syntaxe pojmenování je {PARAMETER NAME}Changed , kde zástupný symbol je název {PARAMETER NAME} parametru. V následujícím příkladu EventCallback<TValue> má název YearChanged .

EventCallback.InvokeAsync vyvolá delegáta přidruženého k vazbě se poskytnutým argumentem a odešle oznámení události pro změněnou vlastnost.

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

Další informace o událostech EventCallback<TValue> a najdete v části EventCallback tohoto ASP.NET Core Blazor zpracování událostí článku.

V následující Parent komponentě year je pole vázané na parametr podřízené Year komponenty. Parametr Year je možné svázat, protože má doprovodnou YearChanged událost, která odpovídá typu Year parametru.

Pages/Parent.razor:

@page "/Parent"

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

Podle konvence může být vlastnost svázaná s odpovídající obslužnou rutinou události zahrnutím atributu přiřazeného obslužné rutině, kde zástupný symbol @bind-{PROPERTY}:event {PROPERTY} je vlastnost . <ChildBind @bind-Year="year" /> je ekvivalentem zápisu:

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

V sofistikovanějším příkladu z reálného světa se používá následující PasswordEntry komponenta:

  • Nastaví <input> hodnotu prvku na password pole.
  • Zpřístupňuje změny vlastnosti nadřazené komponentě pomocí objektu , který předává aktuální hodnotu podřízeného pole Password EventCallback jako svůj password argument.
  • Použije onclick událost k aktivaci ToggleShowPassword metody . Další informace naleznete v tématu ASP.NET Core Blazor zpracování událostí.

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

Komponenta PasswordEntry se používá v jiné komponentě, například v následujícím PasswordBinding příkladu komponenty.

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

Při PasswordBinding počátečním vykreslení komponenty se v uživatelském rozhraní password zobrazí hodnota Not set . Po počátečním vykreslení hodnota odráží změny provedené v hodnotě parametru komponenty password Password v PasswordEntry komponentě.

Poznámka

Předchozí příklad vytvoří vazbu hesla jedním směrem z podřízené komponenty na PasswordEntry nadřazenou PasswordBinding komponentu. Dvousměnná vazba není v tomto scénáři požadavkem, pokud je cílem aplikace mít sdílenou komponentu pro zadávání hesel, aby ji bylo možné opakovaně používat v aplikaci, která pouze předává heslo nadřazenému serveru. Přístup, který umožňuje dvoucestné vazby bez přímého zápisu do parametru podřízené komponenty ,najdete v příkladu komponenty v části Vázání napříč více než dvěma součástmi NestedChild tohoto článku.

Provádění kontrol nebo zachytání chyb v obslužné rutině Následující revidovaná komponenta poskytuje uživateli okamžitou zpětnou vazbu, pokud se v hodnotě hesla používá PasswordEntry mezera.

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

Vytvoření vazby mezi více než dvěma komponentami

Parametry můžete svázat s libovolným počtem vnořených komponent, ale musíte respektovat jednosměnné toky dat:

  • Oznámení o změně se chytou nahoru v hierarchii.
  • Nové hodnoty parametrů prochádí hierarchii .

Běžným a doporučeným přístupem je ukládat pouze podkladová data v nadřazené komponentě, aby nedocházelo k nejasnostem o tom, jaký stav je třeba aktualizovat, jak je znázorněno v následujícím příkladu.

Pages/Parent.razor:

@page "/parent"

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

Upozornění

Obecně se vyhněte vytváření komponent, které zapisují přímo do vlastních parametrů součásti. Předchozí komponenta místo přímého zápisu do svého parametru používá NestedChild BoundValue vlastnost ChildMessage . Další informace naleznete v tématu RazorASP.NET Core Součásti.

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

Alternativní přístup vhodný ke sdílení dat v paměti a mezi komponentami, které nejsou nutně vnořené, najdete v tématu ASP.NET Core Blazor Správa stavu .

Další zdroje informací