#blazor #blazor-server-side #blazor-webassembly
#blazor #blazor-на стороне сервера #blazor-webassembly
Вопрос:
У меня есть layout ( MainLayout.razor
) , и у него есть вызываемый флаг ShowFooter
. На некоторых страницах я хочу иметь возможность установить этот флаг true
равным, а некоторые другие false
— равными.
Мне не удалось найти никаких четких инструкций о том, как страница (то есть компонент с маршрутом) может взаимодействовать со своим макетом. Как это можно / должно быть сделано в Blazor?
Примечание: вы можете предложить иметь 2 макета, один с нижним колонтитулом, а другой без него, но это не решит мою проблему, я хочу иметь возможность показывать и скрывать нижний колонтитул в разное время на одной и той же странице. Кроме того, это всего лишь один сценарий, в котором необходимо обмениваться данными между макетом и страницей. Есть также бесчисленное множество других.
Ответ №1:
Самый простой способ сделать это — определить общедоступное логическое свойство с именем ShowFooter в компоненте MainLaout следующим образом:
public bool ShowFooter {get; set;}
И каскадировать ссылку на компонент MainLaout для заданных компонентов, перенося разметку в CascadingValue
компонент, для атрибута Value которого установлено значение this
, например:
@inherits LayoutComponentBase
<CascadingValue Value="this">
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<div class="content px-4">
@Body
</div>
</div>
</CascadingValue>
@code
{
public bool ShowFooter {get; set;}
protected override void OnInitialized()
{
// Put here code that checks the value of ShowFooter and acts in
// accordance with your dear wishes
}
}
Использование в Index.razor
@code{
// Gets a reference to the MainLayout component
[CascadingParameter]
public MainLayout Layout { get; set; }
protected override void OnInitialized()
{
Layout.ShowFooter= true;
}
}
Комментарии:
1. Примечание: шаблон AppState обрабатывает состояние компонентов в целом..
2. По-видимому, макет не отображается повторно, когда я изменяю общедоступные свойства в компоненте, даже когда я вызываю
StateHasChanged()
метод. Это правда?3. Понял. Спасибо. Кстати, опять же, как я уже сказал, изменение общедоступных свойств layout в других компонентах, например, в событиях пользовательского интерфейса, похоже, не приводит к повторному отображению макета. Это ограничение этого подхода? Или я делаю что-то не так?
4. Нет «ограничения этого подхода». Будьте уверены, что даже Стив Андерсон предложил бы вам написать такой код. Вам нужно следующее: InvokeAsync(() => StateHasChanged()); Примечание: я собираюсь опубликовать в новом ответе простую программу, которая может быть вам полезна. Скопируйте и запустите код. Введите текст в текстовое поле, расположенное в компоненте Index, и вы увидите вводимый текст в заголовке основного описания. Скопируйте код, так как я собираюсь его скоро удалить.
5. Или вы могли бы оставить это для других, чтобы они также могли извлечь выгоду.
Ответ №2:
Есть несколько способов сделать это:
- Самое уродливое: если у вас есть два шаблона, вы можете просто выбрать шаблон, который хотите использовать, со следующим в верхней части страницы / компонента:
@layout NoFooterLayoutName
- Используйте каскадное значение в шаблоне (то, что я бы рекомендовал для вашего scenerio):
<CascadingValue Value="Footer"> <Child /> </CascadingValue>
Пример скрипки:
https://blazorfiddle.com/s/05spcuyk
- Создайте государственную службу и добавьте ее в startup как область действия. Государственная служба с переменной bool нижнего колонтитула, а затем может быть введена в страницы / компоненты и используемая переменная:
В методе startup.cs ConfigureService:
services.AddScoped<AppState>();
Создайте класс AppState.cs где-нибудь в вашем проекте (в идеале в папке Services):
public class AppState
{
public bool ShowFooter { get; set; }
public event Action StateChanged;
private void NotifyStateChanged() => StateChanged?.Invoke();
}
Затем введите его в свою страницу / компоненты, чтобы вы могли изменить элемент ShowFooter, а в своем шаблоне вы можете создать обработчик событий (не уверен, если это необходимо) для того, чтобы вызвать StateHasChanged():
@inject AppState _AppState;
@implements IDisposable
.
.
.
@code{
protected override void OnInitialized()
{
_appState.StateChanged = StateChanged;
}
public void StateChanged()
{
StateHasChanged();
}
public void Dispose()
{
_appState.StateChanged -= StateChanged;
}
}
Комментарии:
1. Спасибо. Мне это нравится. Но может
AppState
ли класс быть статическим классом, а не зарегистрированным как одноэлементный сервис? В чем преимущество использования его в качестве синглтона?2. @Arad Класс AppState не должен быть статическим, внедрение зависимостей создаст экземпляр класса для каждого пользователя / запроса (область действия), классы состояний должны быть предназначены для хранения состояния пользователя — и это может быть не то, что вы хотите, чтобы это делало. Если вы правильно поняли свою проблему, лучшим вариантом может быть использование CascadingValue для отображения / скрытия нижнего колонтитула (bool) в компоненте макета (mainLayout), чтобы страницы могли установить для него все, что им может понравиться, в инициализированном методе переопределения….
3. @Arad … Имейте в виду, однако, что каждая страница должна была бы установить это, или у вас была бы ситуация, когда какая-то страница скроет нижний колонтитул, а затем при переходе на другую страницу, если нижний колонтитул не будет сброшен в true, он останется скрытым.
Ответ №3:
Вы могли бы использовать службу уведомлений и внедрить это в компоненты.
public class NotifyService
{
public event Func<bool, Task> Notify;
public async Task Notify(bool value)
{
if (Notify is object)
{
await Notify.Invoke(value);
}
}
}
А затем зарегистрируйте это как одноэлементный (или ограниченный, если на стороне сервера) в контейнере DI и внедрите это в свои компоненты.