BF Summer sales
Pouze tento týden sleva až 80 % na HTML & CSS a JavaScript
80 % bodů zdarma na online výuku díky naší Letní akci!

Lekce 2 - Blazor - Rozšíření Todo aplikace v .NET Core SPA

V minulé lekci, Blazor - .NET Core SPA s C# .NET i na straně klienta, jsme si uvedli technologii Blazor a vytvořili jednoduchou Todo aplikaci.

V dnešní lekci si rozšíříme úvodní příklad s Todo aplikací o další komponentu TodoList. Ukážeme si také, jak reagovat na události.

Model úkolu - Todo

Nová komponenta TodoList bude zobrazovat seznam úkolů a jako zdroj dat tedy bude mít kolekci úkolů. Pro úkol, který v našem případě obsahuje pouze text a informaci o splnění, ještě nemáme vytvořenou modelovou třídu. Pojďme to nyní napravit.

V Solution Exploreru vytvořme novou složku Model/ a v ní novou třídu s názvem Todo. Do nové třídy přidáme dvě vlastnosti:

public class Todo
{
    public string Text { get; set; }
    public bool Done { get; set; }
}

Nový jmenný prostor Model také "zaregistrujme" v souboru _Imports.razor. To nám usnadní práci s naší novou třídou uvnitř komponent:

@using BlazorITnetwork.Model

Váš jmenný prostor, tedy část BlazorITnetwork, se samozřejmě může jmenovat jinak v závislosti na názvu projektu.

Komponenta TodoList

Ve složce Shared/ vytvořme novou komponentu TodoList. Jak už jsem zmínil výše, tato komponenta bude zobrazovat seznam úkolů, proto jí přidejme parametr typu IList<Todo>. Seznam úkolů zobrazíme s pomocí cyklu foreach a s využitím Bootstrapu jej můžeme obalit třeba do .card:

<div class="card">
    <div class="card-header">
        <h4 class="card-title">Seznam úkolů</h4>
    </div>
    <div class="card-body">
        @if (Todos.Count == 0)
        {
            <span>Seznam je prázdný ...</span>
        }
        else
        {
            foreach (var todo in Todos)
            {
                <TodoItem Text="@todo.Text" Done="@todo.Done" />
            }
        }
    </div>
</div>
@code {
    [Parameter]
    public IList<Todo> Todos { get; set; } = new List<Todo>();
}

Pokud vám Visual Studio neochvějně tvrdí, že třídu Todo nezná, zkuste rebuild aplikace. Případně zkontrolujte výše popsanou úpravu v souboru _Imports.razor.

V souboru Index.razor nyní zobrazíme novou komponentu TodoList. Jako parametr ji předáme seznam úkolů k zobrazení. Ten bychom v reálné aplikaci pravděpodobně načetli z databáze, ale my si jej zde jednoduše vytvoříme přímo v kódu:

<TodoList Todos="@todos"/>
@code{
    IList<Todo> todos = new List<Todo>()
    {
        new Todo() {Text = "Naučit se Blazor na ITnetwork"},
        new Todo() {Text = "Vytvořit vlastní Blazor aplikaci"},
        new Todo() {Text = "Pochopit práci s parametry komponenty", Done = true}
    };
}

Jak vypadá naše aplikace po spuštění je vidět na následujícím obrázku:

Náhled aplikace po spuštění

Přidání nového úkolu

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Rozšíříme naší aplikaci o další funkcionalitu. Umožníme uživateli přidat nový úkol. K tomuto účelu vytvoříme komponentu NewItemEntry.razor. HTML část komponenty se bude skládat z:

  • popisku,
  • vstupního pole pro text a
  • tlačítka pro spuštění akce.

Pokud bude vstupní pole prázdné, tlačítko pro přidání úkolu bude neaktivní. Komponentu opět můžeme naformátovat s pomocí Bootstrapu. Část s kódem bude zatím obsahovat textovou vlastnost pro navázání na vstupní pole a vlastnost jen pro čtení, která znepřístupní tlačítko, pokud bude vstupní pole prázdné:

<div class="input-group input-group-lg">
    <div class="input-group-prepend">
        <span class="input-group-text">Nový úkol</span>
    </div>
    <input type="text" class="form-control" @bind="Text"/>
    <div class="input-group-append">
        <button class="btn btn-secondary @buttonDisabled">Přidat</button>
    </div>
</div>
@code {
    public string Text { get; set; }

    string buttonDisabled => string.IsNullOrEmpty(Text) ? "disabled" : null;
}

Komponentu NewItemEntry si vložíme do komponenty TodoList pod seznam úkolů:

...
<NewItemEntry/>
...

Výsledek vypadá nějak takto:

Neaktivní pole

Než přidáme obsluhu stisknutí tlačítka, pojďme odstranit na první pohled nezjistitelnou chybu. Naše tlačítko má být neaktivní, protože vstupní pole je prázdné, to je v pořádku. Ovšem pokud do vstupního pole napíšeme nějaký text, tlačítko zůstává stále neaktivní a to už v pořádku není. Použili jsme obousměrný binding, tak kde je problém?

Tlačítko se aktivuje až ve chvíli, kdy neprázdné pole opustíme (ztratí focus), například kliknutím na jinou komponentu. Důvodem je, že binding defaultně reaguje na událost onchange, která se vyvolá až při opuštění vstupního pole. My bychom potřebovali reagovat hned po stisku klávesy, takže upravíme událost bindingu na oninput:

...
<input type="text" class="form-control" @bind="Text" @bind:event="oninput"/>
...

Po spuštění se tlačítko aktivuje hned po napsání prvního znaku.

Obsluha kliknutí na tlačítko

V komponentě NewItemEntry nemůžeme při kliknutí na tlačítko přidat položku do seznamu, protože tato komponenta seznam úkolů nemá. Seznam je v nadřazené komponentě TodoList, proto komponenta NewItemEntry pouze "oznámí" nadřazené komponentě, že došlo ke stisku tlačítka a předá jí text ze vstupního pole. Výsledné zpracování této "události" tedy proběhne v komponentě TodoList.

Do komponenty NewItemEntry přidáme parametr OnNewItem typu EventCallback<string> a tuto událost vyvoláme v nové metodě NewItem(), kterou zavoláme na stisk tlačítka:

...
<button class="btn btn-secondary @buttonDisabled" @onclick="NewItem">Přidat</button>
...
@code {
    ...
    [Parameter]
    public EventCallback<string> OnNewItem { get; set; }

    void NewItem()
    {
       OnNewItem.InvokeAsync(Text);
       Text = string.Empty;
    }
}

Místo typu EventCallback<> bychom mohli použít i klíčová slova event a delegate nebo typy Action<>, resp. Func<>. EventCallback je ovšem typ delegáta používaný k vystavení událostí napříč komponentami. A nadřazená komponenta může samozřejmě přiřadit obsluhu události EventCallback podřízené komponenty. Při použití události EventCallback tímto způsobem jsou nadřazené a podřízené komponenty automaticky překresleny při vyvolání události. U ostatních výše zmíněných způsobů by bylo nutné pro správné překreslení volat metodu StateHasChanged().

Do komponenty TodoList přidáme metodu OnNewItem() s parametrem typu string. Tuto metodu použijeme pro obsloužení naší nové události na komponentě NewItemEntry. Jen v ní přidáme nový úkol s předaným textem do seznamu úkolů:

...
<NewItemEntry OnNewItem="@OnNewItem"/>
...
@code {
    ...
    void OnNewItem(string text)
    {
        Todos.Add(new Todo() { Text = text });
    }
}

Vše funguje, jak má :-) :

Reakce na Enter

Komponentu NewItemEntry můžeme upravit tak, aby kromě stisku tlačítka vyvolala akci přidání úkolu také po stisku klávesy Enter. Klidně si to zkuste nejdříve sami ;-)

Jedno z možných řešení je níže:

...
<input type="text" class="form-control" @bind="Text" @bind:event="oninput" @onkeypress="KeyPress"/>
...
@code {
...
    void KeyPress(KeyboardEventArgs e)
    {
        if (e.Key == "Enter" && !string.IsNullOrEmpty(Text))
            NewItem();
    }
}

To je pro dnešní lekci vše. Pokud si chcete procvičovat, zkuste třeba upravit komponentu TodoList tak, aby se po splnění všech úkolů zbarvilo záhlaví zelenou barvou.

V další lekci, Blazor - Binding, se podíváme trochu hlouběji na one-way a two-way binding.


 

Předchozí článek
Blazor - .NET Core SPA s C# .NET i na straně klienta
Všechny články v sekci
Blazor - C# .NET namísto JavaScriptu
Článek pro vás napsal JOF
Avatar
Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!
Aktivity (2)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!