Содержание
Динамические компоненты Razor удобно использовать в том случае, когда мы заранее не знаем как будет строится пользовательский интерфейс. Например, наше приложение может использовать систему виджетов и пользователь сам определяет какие виджеты необходимо вывести на главной странице. В этом случае нам потребуется обеспечить динамическое создание компонентов.
Компонент <DynamicComponent>
<DynamicComponent> — это встроенный компонент Razor, использование которого в разметке выглядит следующим образом:
<DynamicComponent Type="componentType" Parameters="parameters" />
@code {
private Type componentType = ...;
private IDictionary<string, object> parameters = ...;
}
Параметр Type определяет тип компонента, который будет показан пользователю, а Parameters — список параметров, которые будут переданы в компонент. Посмотрим на работу этого компонента на примере.
Пример использования DynamicComponent
В качестве примера, будем размещать на главной странице приложения (компонент Home.razor) различные дочерние компоненты, которые будет выбирать пользователь. Итак, создадим новое приложение Blazor Hybrid и добавим в папку Components следующие компоненты:
1. Компонент Clock.razor (часы)
<h4>Часы</h4>
@if (timer != null)
{
<p>Дата: @time.ToString("D")</p>
<p>Время: @time.ToString("T")</p>
}
@code {
System.Threading.Timer? timer;
DateTime time = DateTime.Now;
private async void SetClock(object stateInfo)
{
time = DateTime.Now;
await InvokeAsync(StateHasChanged);
}
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
timer = new System.Threading.Timer(SetClock, new System.Threading.AutoResetEvent(false), 1000, 1000);
}
}
2. Компонент Message.razor (сообщение)
<div class="alert alert-danger" role="alert">
@Text
</div>
@code{
[Parameter]
public string Text { get; set; } = "Важное сообщение";
}
Третьим компонентов будет выступать уже имеющийся в приложении счётчик (Counter.razor) с небольшими изменениями.
3. Шаблонный компонент Counter.razor (счётчик)
@page "/counter"
<h4>Counter</h4>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
[Parameter]
public EventCallback OnMaxValue { get; set; }
private async Task IncrementCount()
{
if (currentCount == 10)
await OnMaxValue.InvokeAsync();
else
currentCount++;
}
}
Вот эти три компонента мы и будем динамически отображать на главной странице приложения. Теперь перейдем в файл Home.razor и изменим его код следующим образом:
@page "/"
<div class="d-flex justify-content-between">
<div>
<input class="form-check-input" type="checkbox" id="clock" name="clock" @bind-value="showClock" />
<label class="form-check-label" for="clock">Часы</label>
</div>
<div>
<input class="form-check-input" type="checkbox" id="message" name="message" @bind-value="showMessage" />
<label class="form-check-label" for="message">Сообщение</label>
</div>
<div>
<input class="form-check-input" type="checkbox" id="counter" name="counter" @bind-value="showCounter" />
<label class="form-check-label" for="counter">Счётчик</label>
</div>
</div>
@if (showClock)
{
<DynamicComponent Type="typeof(Clock)"></DynamicComponent>
}
@if (showMessage)
{
<DynamicComponent Type="typeof(Message)"></DynamicComponent>
}
@if (showCounter)
{
<DynamicComponent Type="typeof(Counter)"></DynamicComponent>
}
@code{
bool showCounter = false;
bool showClock = false;
bool showMessage = false;
}
Рассмотрим подробнее этот код. В разметке мы определили три html-элемента типа checkbox которым мы сделали привязку к их свойству value, используя три переменные в коде компонента:
bool showCounter = false; bool showClock = false; bool showMessage = false;
Например, для первого чекбокса код разметки выглядит так:
<div>
<input class="form-check-input" type="checkbox" id="clock" name="clock" @bind-value="showClock" />
<label class="form-check-label" for="clock">Часы</label>
</div>
В зависимости от состояния чекбокса мы либо показываем компонент на главной странице приложения, либо прячем:
@if (showClock)
{
<DynamicComponent Type="typeof(Clock)"></DynamicComponent>
}
@if (showMessage)
{
<DynamicComponent Type="typeof(Message)"></DynamicComponent>
}
@if (showCounter)
{
<DynamicComponent Type="typeof(Counter)"></DynamicComponent>
}
Запустим приложение и посмотрим на результат:
Соответственно, когда вы отмечаете чекбокс — компонент появляется, убираете — компонент скрывается. Так в самом простом случае работает встроенный компонент <DynamicComponent>. Как вы могли заметить, наш компонент «Сообщение» выводит сообщение по умолчанию, хотя у этого компонента определен параметр Text, с помощью которого мы можем задавать собственный текст сообщения. Рассмотрим как мы это можем делать с использованием <DynamicComponent>.
Передача параметров в динамические компоненты Razor
Для передачи параметров в динамические компоненты Razor используется такой же механизм, как и при передаче произвольного набора параметров в html-элементы. То есть, мы должны сформировать словарь вида Dictionary<string, object>, где ключом является имя параметра динамического компонента и передать этот словарь в параметре Parameters <DynamicComponent>. Попробуем передать в наш компонент «Сообщение» произвольный текст — перепишем код Home.razor следующим образом:
@page "/"
<div class="d-flex justify-content-between">
<div>
<input class="form-check-input" type="checkbox" id="clock" name="clock" @bind-value="showClock" />
<label class="form-check-label" for="clock">Часы</label>
</div>
<div>
<input class="form-check-input" type="checkbox" id="message" name="message" @bind-value="showMessage" />
<label class="form-check-label" for="message">Сообщение</label>
</div>
<div>
<input class="form-check-input" type="checkbox" id="counter" name="counter" @bind-value="showCounter" />
<label class="form-check-label" for="counter">Счётчик</label>
</div>
</div>
@if (showClock)
{
<DynamicComponent Type="typeof(Clock)"></DynamicComponent>
}
@if (showMessage)
{
<DynamicComponent Type="typeof(Message)" Parameters="messageParameters"></DynamicComponent>
}
@if (showCounter)
{
<DynamicComponent Type="typeof(Counter)"></DynamicComponent>
}
@code{
bool showCounter = false;
bool showClock = false;
bool showMessage = false;
Dictionary<string, object> messageParameters = new()
{
{ "Text","Это текст для сообщения, переданный из Home.razor"}
};
}
В коде компонента мы сформировали словарь с параметрами для компонента:
Dictionary<string, object> messageParameters = new()
{
{ "Text","Это текст для сообщения, переданный из Home.razor"}
};
А в разметке передали этот словарь компоненту:
@if (showMessage)
{
<DynamicComponent Type="typeof(Message)" Parameters="messageParameters"></DynamicComponent>
}
Результат будет следующим:
Здесь мы передали в параметрах обычную строку, однако, при необходимости, мы можем передавать в динамические компоненты различные объекты и методы обратного вызова для обработки событий компонента.
Обработка событий в динамических компонентах
Чтобы продемонстрировать передачу методов обратного вызова в динамические компоненты, воспользуемся компонентом Counter у которого определено следующее событие:
[Parameter]
public EventCallback OnMaxValue { get; set; }
Изменим код Home.razor следующим образом:
@page "/"
<div class="d-flex justify-content-between">
<div>
<input class="form-check-input" type="checkbox" id="clock" name="clock" @bind-value="showClock" />
<label class="form-check-label" for="clock">Часы</label>
</div>
<div>
<input class="form-check-input" type="checkbox" id="message" name="message" @bind:get="showMessage" @bind:set="(value)=>{showMessage=value;}" />
<label class="form-check-label" for="message">Сообщение</label>
</div>
<div>
<input class="form-check-input" type="checkbox" id="counter" name="counter" @bind-value="showCounter" />
<label class="form-check-label" for="counter">Счётчик</label>
</div>
</div>
@if (showClock)
{
<DynamicComponent Type="typeof(Clock)"></DynamicComponent>
}
@if (showMessage)
{
<DynamicComponent Type="typeof(Message)" Parameters="messageParameters"></DynamicComponent>
}
@if (showCounter)
{
<DynamicComponent Type="typeof(Counter)" Parameters="counterParameters"></DynamicComponent>
}
@code{
bool showCounter = false;
bool showClock = false;
bool showMessage = false;
private Dictionary<string, object> counterParameters = new();
public Dictionary<string, object> messageParameters = new()
{
{ "Text","Это текст для сообщения, переданный из Home.razor"}
};
private void OnCounterMaxValue()
{
messageParameters.Clear();
messageParameters.Add("Text", "Счётчик достиг максимального значения");
showMessage = true;
}
protected override void OnInitialized()
{
counterParameters[nameof(Counter.OnMaxValue)] = EventCallback.Factory.Create(this, OnCounterMaxValue);
}
}
Первое, что мы изменили — это установили двухстороннюю привязку для чекбокса, отвечающего за вывод сообщения:
<div>
<input class="form-check-input" type="checkbox" id="message" name="message" @bind:get="showMessage" @bind:set="(value)=>{showMessage=value;}" />
<label class="form-check-label" for="message">Сообщение</label>
</div>
Зачем мы это сделали — показано далее. Второй момент — это мы добавили новый словарь
private Dictionary<string, object> counterParameters = new();
В котором мы будем передавать параметры для компонента Counter.
Третий момент — мы переопределили метод OnInitialized() где создали объект словаря и записали в него передаваемый параметр — метод обратного вызова:
protected override void OnInitialized()
{
counterParameters[nameof(Counter.OnMaxValue)] = EventCallback.Factory.Create(this, OnCounterMaxValue);
}
Здесь стоит обратить внимание на то как передается обработчик события — мы воспользовались методом Create свойства Factory у EventCallback. Свойство Factory представлено объектом типа Event — фабрика для создания экземпляров EventCallback и EventCallback<T>.
В метод Event мы передаем два параметра:
receiver— приемник сообщений — любой объект, обрабатывающий событиеcallback— действиеAction<Object>— собственно, сам метод обратного вызова
Метод обратного вызова у нас выглядит следующим образом:
private void OnCounterMaxValue()
{
messageParameters.Clear();
messageParameters.Add("Text", "Счётчик достиг максимального значения");
showMessage = true;
}
Здесь мы назначаем компоненту Message новое значение параметра Text и программно включаем чекбокс для показа сообщения, изменяя значение у showMessage . Именно по этому мы изменили простую привязку к свойству value чекбокса на двухстороннюю. Если бы мы этого не сделали, то программное изменение значения showMessage никак бы не влияло на состояние чекбокса.
Теперь запустим приложение и включим счётчик. Вид приложения будет следующим:
Как только счётчик достигнет значения 10, на одиннадцатом клике вид приложения изменится следующим образом:
Таким образом, можно сказать, что используя параметры динамического компонента, мы успешно передали в него метод обратного вызова для обработки событий. При этом, вы можете сразу показать сообщение послед запуска приложения, а потом прокликать счётчик и на одиннадцатом клике сообщение автоматически поменяется.
Итого
Динамические компоненты Razor в разметке представлены компонентами <DynamicComponent> и позволяют отображать обычные компоненты Razor в зависимости от различных факторов, которые трудно или невозможно предугадать заранее. В динамические компоненты можно передавать как обычные параметры, представленные простыми типами или целыми объектами, так и методы обратного вызова для обработки событий в дочерних компонентах.



