IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 3 - Blazor - Binding

V předchozí lekci, Blazor - Rozšíření Todo aplikace v .NET Core SPA, jsme v naší Todo aplikaci pokračovali a ukázali si, jak správně reagovat na události.

Dnes si uvedeme, co je to vlastně binding a jaká je výhoda two-way bindingu. Názorně si vše ukážeme na dalším příkladu webové aplikace v Blazor a C# .NET.

Binding

Binding je v podstatě vazba mezi proměnnou v kódu a jejím využitím v HTML části komponenty nebo v podřízené komponentě. Binding může být:

  • Jednosměrný - Jednosměrná vazba je užitečná, když potřebujeme reprezentovat data v uživatelském rozhraní pouze pro čtení.
  • Obousměrný - Obousměrná vazba se používá, pokud potřebujeme aktualizovat stav komponent při změně hodnoty proměnné. Proto se obvykle používá u vstupních komponent v HTML, kde změna hodnoty uživatelem může přímo ovlivnit chování dalších komponent nebo prvků na stránce.

Příklad

Jak je binding implementován v Blazoru si pro lepší pochopení ukážeme na jednoduchém příkladu. Budeme převádět uživatelem zadanou hodnotu z palců na centimetry a obráceně. Vytvořme si novou stránku (tedy komponentu ve složce Pages/) s názvem Converter.

Routa

Protože se jedná o stránku, nastavme ji hned na prvním řádku routu (s pomocí direktivy @page) pro definici relativní URL, na které ji můžeme zobrazit:

@page "/converter"

Odkaz do menu

Abychom nemuseli výše zadanou cestu psát v prohlížeči ručně (i když si můžete vyzkoušet, že to funguje), přidejme si na ni odkaz do menu. To je jednoduchá úprava komponenty NavMenu. "Naklonujme" si blok <li>...</li> v seznamu odkazů a upravme cíl (href), ikonu (oi-...) a text odkazu:

<li class="nav-item px-3">
    <NavLink class="nav-link" href="converter">
        <span class="oi oi-transfer" aria-hidden="true"></span> Převodník
    </NavLink>
</li>

Po spuštění aplikace nám v menu přibude odkaz na naši novou stránku:

Blazor - C# .NET namísto JavaScriptu

Jednosměrný binding

Přidejme si do kódu stránky proměnnou palce typu double a do HTML vstupní pole a text, do kterých bude její hodnota navázána:

<input value="@palce" type="number" />
<p>Hodnota v palcích je: @palce</p>
@code {
    double palce = 1;
}

Zamysleme se nad tím, jak bude komponenta fungovat. Je zřejmé, že po spuštění by se ve vstupním poli měla objevit výchozí hodnota proměnné (v našem případě 1). Text pod vstupním polem by měl být "Hodnota v palcích je: 1".

Ale co se stane, když ve vstupním poli hodnotu změníme? Pro někoho možná překvapivě se změní právě jen hodnota ve vstupním poli, ale nikoliv v textu pod ním. Tento způsob vazby je právě označován jako jednosměrná vazba. Jazykem programátorů bychom také mohli říci, že jsme do části HTML předali proměnnou palce nikoliv referencí, ale hodnotou.

Reakce na událost onchange

My samozřejmě chceme, aby se při změně hodnoty ve vstupním poli odpovídajícím způsobem změnil i text pod vstupním polem. Musíme proto k jednosměrné vazbě přidat ještě reakci na událost onchange vstupního pole. Do této metody můžeme přidat logiku, která aktualizuje hodnotu v proměnné palce:

void UpdateValue(ChangeEventArgs e)
{
    double val = 0;
    double.TryParse(e.Value.ToString(), out val);
    palce = val;
}

A tuto metodu přiřadíme k události onchange vstupního pole:

<input value="@palce" type="number" @onchange="UpdateValue"/>

Můžeme vyzkoušet, že po spuštění aplikace se bude hodnota v textu automaticky aktualizovat podle hodnoty ve vstupním poli.

Obousměrný binding

Jak vidíme, náš příklad je funkční i s jednosměrnou vazbou spolu s ošetřením události onchange. Existuje ale jednodušší způsob a tím je právě obousměrná vazba.

Tuto vazbu vytvoříme s pomocí direktivy @bind. Zajímavé na naší předchozí implementaci je, že funguje velice podobně jako interní implementace @bind. Ta se také defaultně váže na atribut value, používá jako výchozí událost onchange, po jejím vyvolání aktualizuje hodnotu v navázané proměnné a při tom ji automaticky převádí na správný datový typ.

S použitím obousměrné vazby je samozřejmě zápis kratší a přehlednější:

<input @bind="palce" type="number"/>
<p>Hodnota v palcích je: @palce</p>
@code {
    double palce = 1;
}

Pole pro centimetry

U obousměrné vazby zůstaneme a rozšíříme komponentu o další vstupní pole a příslušnou proměnnou pro centimetry. Zobrazovaný text můžeme již odstranit:

<label>Palce</label>
<input type="number" @bind="palce" />
<span> = </span>
<input type="number" @bind="centimetry" />
<label>Centimetry</label>
@code {
double palce = 1;
double centimetry = 2.54;
}

Každý vstup je svázán se svou proměnnou, ale chybí logika, která při změně hodnoty jedné z proměnných přepočítá a upraví tu druhou. K tomu můžeme použít klasické properties ze C# a jejich setter. Úprava pro přepočítání hodnoty v centimetrech po změně hodnoty v palcích by mohla vypadat nějak takto:

double palce = 1;
public double Palce
{
    get => palce;
    set
    {
        centimetry = value * 2.54;
        palce = value;
    }
}

Potom ale nesmíme zapomenout na vstupní pole místo proměnné palce navázat vlastnost Palce:

<input type="number" @bind="Palce" />

Převod z palců na centimetry by nyní měl fungovat. Pokud obdobnou úpravu uděláme i pro centimetry, naše jednoduchá komponenta bude plně funkční.

Další možnosti

Jak jsme zmínili výše, binding se defaultně váže na atribut value a používá jako výchozí událost onchange. Obě tyto vlastnosti vazby lze jednoduše upravit.

Pro změnu vázaného atributu lze použít místo zkrácené direktivy @bind="variable" tvar @bind-propertyName="variable". Například námi použitý zápis @bind="Palce" by mohl mít také tvar @bind-value="Palce" se stejným významem.

Změna výchozí události vazby se provede přidáním dalšího parametru ve tvaru @bind-propertyName:event="eventName". Pokud bychom tedy v našem případě chtěli aktualizovat hodnotu proměnné hned po stisku klávesy, nikoliv až po opuštění komponenty, použili bychom delší tvar direktivy @bind a připsali bychom další parametr @bind-value:event="oninput".

Vazba na parametr podřízené komponenty

Na samostatném příkladu si můžeme ukázat binding do podřízené komponenty:

<h2>Child Component</h2>
<p>Year: @Year</p>
@code {
    [Parameter]
    public int Year { get; set; }

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

EventCallback<>

Pokud chceme použít binding do podřízené komponenty, musí v ní být kromě příslušné public property také parametr typu EventCallback<...> se stejným datovým typem.

Jmenná konvence nám také důrazně doporučuje pojmenovat tento druhý parametr stejně jako první parametr s přídavkem Changed, tak jak vidíme na předchozím příkladu - vlastnost Year a příslušný EventCallback YearChanged. Pokud tuto konvenci dodržíme, nemusíme při použití komponenty druhý parametr vůbec specifikovat a Blazor si ho přiřadí automaticky:

@page "/ParentComponent"

<h1>Parent Component</h1>

<p>ParentYear: @ParentYear</p>

<ChildComponent @bind-Year="ParentYear" />

<button class="btn btn-primary" @onclick="IncrementYear">
    Increment Year
</button>
@code {
    [Parameter]
    public int ParentYear { get; set; } = 1976;

    private void IncrementYear()
    {
        ParentYear++;
    }
}

Rodičovská komponenta

V rodičovské komponentě máme vlastnost ParentYear, její hodnotu navážeme na parametr Year podřízené komponenty (a ještě jednou si ji zobrazíme přímo v této komponentě). Po kliknutí na tlačítko změníme v rodičovské komponentě hodnotu ParentYear a v prohlížeči uvidíme, že změna nastane jak v přímém zobrazení, tak v podřízené komponentě.

Změna v rodičovské komponentě

Až potud vše funguje dobře. Problém nastane, pokud bychom uvnitř podřízené komponenty chtěli hodnotu navázaného parametru Year měnit. Takzvané zřetězení vazeb bohužel nefunguje, takže pokud bychom i uvnitř podřízené komponenty použili obousměrný binding na nějaké vstupní pole <input type="number" @bind="Year" />, o změně hodnoty se rodičovská komponenta nedozví. To lze naštěstí obejít použitím jednosměrné vazby a vyvoláním události podobně jako na začátku dnešní lekce:

<h2>Child Component</h2>

<input type="number" value="@Year" @onchange="OnYearChanged" />
@code {
    [Parameter]
    public int Year { get; set; }

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

    private void OnYearChanged(ChangeEventArgs e)
    {
        int year = Year;
        int.TryParse(e.Value.ToString(), out year);
        if (year != Year)
        {
            Year = year;
            YearChanged.InvokeAsync(year);
        }
    }
}

Nyní by měl fungovat binding z rodičovské komponenty do podřízené i obráceně :-)

To je pro tuto lekci vše. Příště se podíváme podrobněji na komponenty. Náměty na další lekce můžete psát do komentářů ;-)

V další lekci, Blazor - Komponenty, se podrobněji podíváme na komponenty a možnosti, které nám nabízí parametry.


 

Předchozí článek
Blazor - Rozšíření Todo aplikace v .NET Core SPA
Všechny články v sekci
Blazor - C# .NET namísto JavaScriptu
Přeskočit článek
(nedoporučujeme)
Blazor - Komponenty
Článek pro vás napsal JOF
Avatar
Uživatelské hodnocení:
26 hlasů
Aktivity