3. diel - Blazor - Binding
V predchádzajúcej lekcii, Blazor - Rozšírenie Todo aplikácie v .NET Core SPA , sme v našej Todo aplikácii pokračovali a ukázali si, ako správne reagovať na udalosti.
Dnes si uvedieme, čo je to vlastne binding a aká je výhoda two-way binding. Názorne si všetko ukážeme na ďalšom príklade webové aplikácie v Blazor a C# .NET.
Binding
Binding je v podstate väzba medzi premennou v kóde a jej využitím v HTML časti komponenty alebo v podriadenej komponente. Binding môže byť:
- Jednosmerný - Jednosmerná väzba je užitočná, keď potrebujeme reprezentovať dáta v používateľskom rozhraní len na čítanie.
- Obojsmerný - Obojsmerná väzba sa používa, ak potrebujeme aktualizovať stav komponentov pri zmene hodnoty premennej. Preto sa obvykle používa pri vstupných komponentov v HTML, kde zmena hodnoty užívateľom môže priamo ovplyvniť správanie ďalších komponentov alebo prvkov na stránke.
Príklad
Ako je binding implementovaný v Blazoru si pre lepšie pochopenie ukážeme
na jednoduchom príklade. Budeme prevádzať užívateľom zadanú hodnotu z
palcov na centimetre a obrátene. Vytvorme si novú stránku (teda komponent v
zložke Pages/
) s názvom Converter
.
Ruta
Pretože sa jedná o stránku, nastavme ju hneď na prvom riadku route (s
pomocou direktívy @page
) pre definíciu relatívnej URL, na ktorej
ju môžeme zobraziť:
@page "/converter"
Odkaz do menu
Aby sme nemuseli vyššie zadanú cestu písať v prehliadači ručne (aj
keď si môžete vyskúšať, že to funguje), pridajme si na ňu odkaz do menu.
To je jednoduchá úprava komponenty NavMenu
. "Naklonujte" si blok
<li>...</li>
v zozname odkazov a upravme cieľ
(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 spustení aplikácie nám v menu pribudne odkaz na našu novú stránku:
Jednosmerný binding
Pridajme si do kódu stránky premennú palce
typu
double
a do HTML vstupné pole a text, do ktorých bude jej hodnota
nadviazaná:
<input value="@palce" type="number" /> <p>Hodnota v palcích je: @palce</p>
@code { double palce = 1; }
Zamyslime sa nad tým, ako bude komponent fungovať. Je zrejmé, že po
spustení by sa vo vstupnom poli mala objaviť východisková hodnota premennej
(v našom prípade 1
). Text pod vstupným poľom by mal byť
"Hodnota v palcích je: 1"
.
Ale čo sa stane, keď vo vstupnom poli hodnotu zmeníme? Pre niekoho možno
prekvapivo sa zmení práve len hodnotu vo vstupnom poli, ale nie v texte pod
ním. Tento spôsob väzby je práve označovaný ako jednosmerná
väzba. Jazykom programátorov by sme tiež mohli povedať, že sme do
časti HTML odovzdali premennú palce
nie referencií, ale
hodnotou.
Reakcia na udalosť
onchange
My samozrejme chceme, aby sa pri zmene hodnoty vo vstupnom poli
zodpovedajúcim spôsobom zmenil aj text pod vstupným poľom. Musíme preto k
jednosmernej väzbe pridať ešte reakciu na udalosť onchange
vstupného poľa. Do tejto metódy môžeme pridať logiku, ktorá aktualizuje
hodnotu v premennej palce
:
void UpdateValue(ChangeEventArgs e) { double val = 0; double.TryParse(e.Value.ToString(), out val); palce = val; }
A túto metódu priradíme k udalosti onchange
vstupného
poľa:
<input value="@palce" type="number" @onchange="UpdateValue"/>
Môžeme vyskúšať, že po spustení aplikácie sa bude hodnota v texte automaticky aktualizovať podľa hodnoty vo vstupnom poli.
Obojsmerný binding
Ako vidíme, náš príklad je funkčný aj s jednosmernou väzbou spolu s
ošetrením udalosti onchange
. Existuje ale jednoduchší spôsob a
tým je práve obojsmerná väzba.
Túto väzbu vytvoríme s pomocou direktívy @bind
. Zaujímavé
na našej predchádzajúcej implementáciu je, že funguje veľmi podobne ako
interný implementácie @bind
. Tá sa tiež defaultne viaže na
atribút value
, používa ako predvolené udalosť
onchange
, po jej vyvolaní aktualizuje hodnotu v naviazané
premennej a pri tom ju automaticky prevádza na správny dátový typ.
S použitím obojsmerné väzby je samozrejme zápis kratšie a prehľadnejšie:
<input @bind="palce" type="number"/> <p>Hodnota v palcích je: @palce</p>
@code { double palce = 1; }
Pole pre centimetre
U obojsmerné väzby zostaneme a rozšírime komponent o ďalšie vstupné polia a príslušnú premennú pre centimetre. Zobrazovaný text môžeme už odstrániť:
<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 zviazaný so svojou premennou, ale chýba logika, ktorá pri zmene hodnoty jednej z premenných prepočíta a upraví tú druhú. K tomu môžeme použiť klasické properties zo C# a ich setter. Úprava pre prepočítanie hodnoty v centimetroch po zmene hodnoty v palcoch by mohla vyzerať nejako takto:
double palce = 1; public double Palce { get => palce; set { centimetry = value * 2.54; palce = value; } }
Potom ale nesmieme zabudnúť na vstupné pole miesto premenné
palce
nadviazať vlastnosť Palce
:
<input type="number" @bind="Palce" />
Prevod z palcov na centimetre by teraz mal fungovať. Ak obdobnú úpravu urobíme aj pre centimetre, naša jednoduchá komponent bude plne funkčný.
Ďalšie možnosti
Ako sme spomenuli vyššie, binding sa defaultne viaže na atribút
value
a používa ako predvolené udalosť onchange
.
Obe tieto vlastnosti väzby možno jednoducho upraviť.
Pre zmenu viazaného atribútu možno použiť namiesto skrátenej direktívy
@bind="variable"
tvar @bind-propertyName="variable"
.
Napríklad nami použitý zápis @bind="Palce"
by mohol mať tiež
tvar @bind-value="Palce"
s rovnakým významom.
Zmena predvolenej udalosti väzby sa vykoná pridaním ďalšieho parametra v
tvare @bind-propertyName:event="eventName"
. Ak by sme teda v našom
prípade chceli aktualizovať hodnotu premennej hneď po stlačení klávesy,
nie až po opustení komponenty, použili by sme dlhší tvar direktívy
@bind
a pripísali by sme ďalšie parameter
@bind-value:event="oninput"
.
Väzba na parameter podriadenej komponenty
Na samostatnom príklade si môžeme ukázať binding do podriadenej komponenty:
<h2>Child Component</h2> <p>Year: @Year</p>
@code { [Parameter] public int Year { get; set; } [Parameter] public EventCallback<int> YearChanged { get; set; } }
EventCallback<>
Ak chceme použiť binding do podriadenej komponenty, musí v nej byť okrem
príslušnej public
property tiež parameter typu
EventCallback<...>
s rovnakým dátovým typom.
Menná konvencie nám tiež dôrazne odporúča pomenovať tento druhý
parameter rovnako ako prvý parameter s prídavkom Changed
, tak ako
vidíme na predchádzajúcom príklade - vlastnosť Year
a
príslušný EventCallback
YearChanged
. Ak túto
konvenciu dodržíme, nemusíme pri použití komponenty druhý parameter vôbec
špecifikovať a Blazor si ho priradí 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čovskej komponente máme vlastnosť ParentYear
, jej
hodnotu nadviažeme na parameter Year
podriadené komponenty (a
ešte raz si ju zobrazíme priamo v tejto komponente). Po kliknutí na tlačidlo
zmeníme v rodičovskej komponente hodnotu ParentYear
a v
prehliadači uvidíme, že zmena nastane tak v priamom zobrazenie, tak v
podriadenej komponente.
Zmena v rodičovskej komponente
Až potiaľto všetko funguje dobre. Problém nastane, ak by sme vnútri
podriadenej komponenty chceli hodnotu naviazaného parametra Year
meniť. Takzvané zreťazenie väzieb bohužiaľ nefunguje, takže ak by sme aj
vo vnútri podriadené komponenty použili obojsmerný binding na nejaké
vstupné pole <input type="number" @bind="Year" />
, o zmene
hodnoty sa rodičovská komponent nedozvie. To možno našťastie obísť
použitím jednosmernej väzby a vyvolaním udalosti podobne ako na začiatku
dnešnej lekcie:
<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); } } }
Teraz by mal fungovať binding z rodičovskej komponenty do podriadenej aj obrátene:-)
To je pre túto lekciu všetko. Nabudúce sa pozrieme podrobnejšie na komponenty. Námety na ďalšie lekcie môžete písať do komentárov;-)
V ďalšej lekcii, Blazor - Komponenty , sa podrobnejšie pozrieme na komponenty a možnosti, ktoré nám ponúka parametre.