Blazor - budowa prostej aplikacji cz. 2
Kolejna część wpisu na temat budowy prostej aplikacji w Blazor.

Kontynuacja
Na początku zajmijmy się dodaniem stanu przechowującego wszystkie zadania. Zrobię to w Pages/Home.razor.
Dodaję w bloku @code nowy atrybut Items. Dodatkowo dodam też pętlę foreach do wypisywania wszystkich zadań.
@page "/"
@using Todo.Components
<PageTitle>Home</PageTitle>
@foreach (var item in Items)
{
<TodoItem Item="@item"/>
}
@code {
private List<ListItem> Items = [];
}
Brakuje teraz najważniejszej opcji, czyli dodawania zadań. Przy pomocy Radzen tworzę prostą strukturę pod input i przycisk.
@page "/"
@using Todo.Components
<PageTitle>Home</PageTitle>
<h1>Your list</h1>
<RadzenStack Orientation="Orientation.Horizontal" Wrap="FlexWrap.NoWrap" class="rz-mb-10">
<RadzenTextBox Placeholder="Enter text" Style="width: 100%" aria-label="Default TextBox"/>
<RadzenButton Style="width:150px;" Text="Add" ButtonStyle="ButtonStyle.Primary"/>
</RadzenStack>
@foreach (var item in Items)
{
<TodoItem Item="@item"/>
}
@code {
private List<ListItem> Items = [];
}
Teraz musimy jakoś uzyskać dostęp do zawartości inputa. Służy do tego parametr @bind-value,
który podpina zawartość inputa pod dany atrybut. Działa on tak, jak ustawienie value i onChange w React.
<RadzenTextBox @bind-Value="@Input" .../>
...
@code {
...
private string Input = "";
}
Tworzę teraz metodę HandleAdd która zajmie się obsługą dodania nowego zadania.
private async Task HandleAdd()
{
Items.Insert(0, new ListItem { Content = Input }); // chcemy, by nowe zadanie było na górze
Input = "";
}
By podpiąć ją pod zdarzenie click na przycisku, używa się @onclick, lecz jako, iż używamy przycisku z Radzen to owy
przycisk przyjmuje własny parametr Click.
<RadzenButton Click="@HandleAdd"/>
Teraz, po wpisaniu treści zadania i wciśnięciu przycisku, pojawia się na górze nowe zadanie.
Fajnie, aby istniała teraz możliwość oznaczenia zadania jako wykonane.
Przechodzimy do Components/TodoItem.razor i odpowiednio edytujemy strukturę komponentu, dodając checkbox.
Następnie bindujemy @Item.IsFinished do niego.
...
<RadzenStack ...>
<RadzenText >@Item.Content</RadzenText>
<RadzenCheckBox TValue="bool" @bind-Value="@Item.IsFinished"/>
</RadzenStack>
...
I w przeglądarce zobaczymy, że wszystko działa.
Na grafice z początku mogłeś zobaczyć podział na "To do" oraz "Finished".
Wprowadźmy go w najprostszy możliwy sposób. Znów wracamy do strony Home.
<RadzenText class="rz-mt-4" Visible="@Items.Exists(i => !i.IsFinished)">To do</RadzenText>
@foreach (var item in Items.Where(i => !i.IsFinished))
{
<TodoItem Item="@item"/>
}
<RadzenText class="rz-mt-4" Visible="@Items.Exists(i => i.IsFinished)">Finished</RadzenText>
@foreach (var item in Items.Where(i => i.IsFinished))
{
<TodoItem Item="@item"/>
}
Większość powinna być zrozumiała. Jedynie Visible może wymagać dodatkowego wyjaśnienia. Jest to parametr z Radzena,
który pozwala warunkowo wyświetlać dany komponent.
Teraz jak sprawdzimy przeglądarkę, to okaże się, że... nie działa. Niby da się kliknąć checkboxa, ale zadanie nie zmienia sekcji,
tzn. pozostaje w to do. W komponencie TodoItem zmieniamy jeden z elementów listy rodzica, ale ten rodzic nic nie wie o tej zmianie,
z tego powodu nie dochodzi do ponownego renderowania rodzica.
Spróbujmy potwierdzić tą diagnozę — wymuśmy ponowne renderowanie rodzica.
Dodamy sobie w Pages/Home odpowiednik useEffect który ustawi interwał wymuszający rerender co 3 sekundy.
Rozszerzamy metodę OnInitializedAsync w bloku @code.
protected override async Task OnInitializedAsync()
{
while (true)
{
StateHasChanged(); // metoda wbudowana (odziedziczona), służy do wymuszenia rerenderu
await Task.Delay(3000);
}
}
Po trzech sekundach od zaznaczenia checkboxa zadanie przeskakuje do Finished. Diagnoza potwierdzona.
By naprawić ten problem, skorzystamy z EventCallback. Utwórzmy sobie nowy parametr w TodoItem.
[Parameter]
public EventCallback<ListItem> HandleToggle { get; set; }
A następnie przekażmy go w rodzicu.
...
<TodoItem Item="@item" HandleToggle="@HandleToggle"/>
...
@code {
private async Task HandleToggle(ListItem item)
{
item.Toggle();
}
...
}
Teraz możecie się zastanawiać, dlaczego EventCallback a nie zwykła funkcja, oraz dlaczego zwykła metoda magicznie
zamienia się w EventCallback. EventCallback to w uproszczeniu nakładka na funkcję, która dodatkowo wywołuje rerender.
Blazor przy przekazaniu funkcji jako parametru automatycznie konwertuje ją na EventCallback.
Teraz użyjmy jej w TodoItem.
<RadzenCheckBox TValue="bool" Value="@Item.IsFinished" Change="@(() => HandleToggle.InvokeAsync(Item))"/>
I zobaczymy, że wszystko działa, jak powinno. Operację usuwania zadania implementuje się w podobny sposób, więc to pominę.
Część ostatnia: kliknij