Как осуществляется передача данных от родительских компонентов в дочерние с использованием каскадных параметров в Blazor мы уже знаем. При этом, вполне возможна необходимость и в обратном — передача данных от дочернего компонента в родительский. Сегодня мы рассмотрим вопрос про совместное использование данных дочерними и родительскими компонентами в Blazor и научимся передавать данные из дочернего компонента к предку.
Рассмотрим следующую, достаточно распространенную, ситуацию: в приложении есть список сущностей (например, книг, телефонов и т.д.), каждая сущность имеет свой набор параметров, которые необходимо отобразить пользователю на экране. Соответственно, в приложении у нас есть два компонента:
- дочерний — представляет собой конкретную сущность
- родительский — список сущностей (это может быть как обычный список, так и таблица — сути это не меняет)
у нас есть задача — сортировать список сущностей по различным признакам, которые пользователь выбирает, например, кликая мышкой по соответствующему параметру сущности в списке.
Создадим такое приложение Blazor Hybrid, используя в качестве сущности, например, телефон. Для начала, создадим необходимую модель (класс) телефона. Для этого, создадим в папке Data файл Phone.cs со следующим содержимым:
namespace PhoneManager.Data
{
public class Phone
{
/// <summary>
/// Название телефона
/// </summary>
public string Name { get; set; }
/// <summary>
/// Операционная система
/// </summary>
public string OS { get; set; }
/// <summary>
/// Цена телефона
/// </summary>
public int Price { get; set; }
}
}
Теперь создадим компонент Razor, который будет представлять информацию по одному телефону. Назовем его PhoneItem.razor:
@using PhoneManager.Data;
@if (PhoneData != null)
{
<div class="container">
<div class="row-sm-12" @onclick="@(()=>PhoneElementClick(SortData.Name))">
<b>@PhoneData.Name</b>
</div>
<div class="col-sm-6">
Операционная система: <button class="btn btn-link" @onclick="@(()=>PhoneElementClick(SortData.OS))">@PhoneData.OS</button>
</div>
<div class="col-sm-6" @onclick="@(()=>PhoneElementClick(SortData.Price))">
Цена: @PhoneData.Price
</div>
</div>
}
@code {
[Parameter]
public Phone PhoneData { get; set; }
[Parameter]
public EventCallback<SortData> PhoneClick { get; set; }
public async Task PhoneElementClick(SortData data)
{
await PhoneClick.InvokeAsync(data);
}
}
Рассмотрим более подробно этот компонент. Во-первых, у компонента определены два параметра:
public Phone PhoneData { get; set; }
Содержит информацию по телефону, которую необходимо показать пользователю.
[Parameter]
public EventCallback<SortData> PhoneClick { get; set; }
С помощью этого параметра компонента Blazor мы будем вызывать метод родительского компонент для сортировки списка элементов. При этом, тип SortData будет содержать сведения о том, по какому критерию сортируется список и выглядит следующим образом:
public enum SortData
{
Name,
Price,
OS
}
У некоторых элементов <div> в разметке компонента определены обработчики события click:
@onclick="@(()=>PhoneElementClick(SortData.Name))
Таким образом, клик по элементу будет вызывать метод родительского компонента, который мы передадим дочернему в параметре PhoneClick.
Напишем родительский компонент PhoneList:
@using PhoneManager.Data
<h3>Список телефонов</h3>
@if (Phones != null)
{
@sort
<br>
<div class="container">
@foreach (Phone phone in Phones)
{
<div class="row-sm">
<PhoneItem PhoneData="@phone" PhoneClick="@ClickPhone"></PhoneItem>
<hr>
</div>
}
</div>
}
@code {
[Parameter]
public List<Phone> Phones { get; set; }
string sort = "";
private void ClickPhone(SortData data)
{
switch (data)
{
case SortData.Name:
{
Phones = Phones.OrderBy(o => o.Name).ToList();
sort = "Список отсортирован по названию";
break;
}
case SortData.OS:
{
Phones = Phones.OrderBy(o => o.OS).ToList();
sort = "Список отсортирован по операционной системе";
break;
}
case SortData.Price:
{
Phones = Phones.OrderBy(o => o.Price).ToList();
sort = "Список отсортирован по цене";
break;
}
}
}
}
Этот компонент принимает один параметр — список телефонов, а также, содержит метод обратного вызова (ClickPhone), который будет срабатывать при кликах на определенные элементы в компоненте PhoneItem. Метод обратного вызова назначается в этой строке:
<PhoneItem PhoneData="@phone" PhoneClick="@ClickPhone"></PhoneItem>
Теперь откроем главную страницу приложения (Index.razor) и добавим на неё наш компонент PhoneList:
@page "/"
@using PhoneManager.Data
<PhoneList Phones="@phones"></PhoneList>
@code
{
List<Phone> phones = new()
{
new() { Name = "Xiaomi", OS = "Android 13", Price = 40000 },
new() { Name = "Samsung", OS = "Android 14", Price = 50000 },
new() { Name = "LG", OS = "Android 9", Price = 60000 },
new() { Name = "Honor", OS = "Android 10", Price = 12000 },
new() { Name = "IPhone", OS = "iOS", Price = 120000 },
};
}
Здесь мы в коде компонента создаем список телефонов, а в разметке — передаем его компоненту PhoneList. Запустим приложение и убедимся, что всё работает:

Теперь нажмем на название телефона, операционную систему или цену и увидим, что список отсортируется по заданному критерию:

Итого
Таким образом, совместное использование данных дочерними и родительскими компонентами в Blazor осуществляется путем использования методов обратного вызова, которые назначаются дочерним компонентам через структуру EventCallback<T>. Используя EventCallback<T>, мы можем передавать в родительский компонент любую необходимую информацию — строки, числа, объекты и так далее. Родительский компонент будет обрабатывать полученную информацию и автоматически перерисовывать себя без дополнительных вызовом метода StateHasChanged.