Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a Política de Suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.
Importante
Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.
Para a versão atual, consulte a versão .NET 9 deste artigo.
Este artigo explica como fazer o fluxo de dados de um componente ancestral Razor para componentes descendentes.
Valores e parâmetros em cascata proporcionam uma forma conveniente de fluir dados para baixo em uma hierarquia de componentes, de um componente ancestral para qualquer número de componentes descendentes. Ao contrário dos Parâmetros de componente, valores e parâmetros em cascata não exigem uma designação de atributo para cada componente descendente em que os dados são consumidos. Os valores e parâmetros em cascata também permitem que os componentes se coordenem uns com os outros em uma hierarquia de componentes.
Observação
Os exemplos de código neste artigo adotam NRTs (tipos de referência anuláveis) e análise estática de estado nulo do compilador do .NET, que têm suporte no ASP.NET Core no .NET 6 ou posterior. Ao direcionar para o .NET 5 ou anterior, remova a designação de tipo nulo (?
) dos tipos CascadingType?
, @ActiveTab?
, RenderFragment?
, ITab?
, TabSet?
e string?
nos exemplos do artigo.
Valores em cascata no nível raiz
Os valores em cascata no nível raiz podem ser registrados para toda a hierarquia de componentes. Há suporte para valores em cascata nomeados e assinaturas para notificações de atualização.
A classe a seguir é usada nos exemplos desta seção.
Dalek.cs
:
// "Dalek" ©Terry Nation https://www.imdb.com/name/nm0622334/
// "Doctor Who" ©BBC https://www.bbc.co.uk/programmes/b006q2x0
namespace BlazorSample;
public class Dalek
{
public int Units { get; set; }
}
// "Dalek" ©Terry Nation https://www.imdb.com/name/nm0622334/
// "Doctor Who" ©BBC https://www.bbc.co.uk/programmes/b006q2x0
namespace BlazorSample;
public class Dalek
{
public int Units { get; set; }
}
Os seguintes registros são feitos no arquivo Program
do aplicativo com AddCascadingValue:
-
Dalek
com um valor de propriedade paraUnits
que é registrado como um valor em cascata fixo. - Um segundo registro
Dalek
com um valor de propriedade diferente paraUnits
é nomeado "AlphaGroup
".
builder.Services.AddCascadingValue(sp => new Dalek { Units = 123 });
builder.Services.AddCascadingValue("AlphaGroup", sp => new Dalek { Units = 456 });
O componente Daleks
a seguir exibe os valores em cascata.
Daleks.razor
:
@page "/daleks"
<PageTitle>Daleks</PageTitle>
<h1>Root-level Cascading Value Example</h1>
<ul>
<li>Dalek Units: @Dalek?.Units</li>
<li>Alpha Group Dalek Units: @AlphaGroupDalek?.Units</li>
</ul>
<p>
Dalek© <a href="https://www.imdb.com/name/nm0622334/">Terry Nation</a><br>
Doctor Who© <a href="https://www.bbc.co.uk/programmes/b006q2x0">BBC</a>
</p>
@code {
[CascadingParameter]
public Dalek? Dalek { get; set; }
[CascadingParameter(Name = "AlphaGroup")]
public Dalek? AlphaGroupDalek { get; set; }
}
@page "/daleks"
<PageTitle>Daleks</PageTitle>
<h1>Root-level Cascading Value Example</h1>
<ul>
<li>Dalek Units: @Dalek?.Units</li>
<li>Alpha Group Dalek Units: @AlphaGroupDalek?.Units</li>
</ul>
<p>
Dalek© <a href="https://www.imdb.com/name/nm0622334/">Terry Nation</a><br>
Doctor Who© <a href="https://www.bbc.co.uk/programmes/b006q2x0">BBC</a>
</p>
@code {
[CascadingParameter]
public Dalek? Dalek { get; set; }
[CascadingParameter(Name = "AlphaGroup")]
public Dalek? AlphaGroupDalek { get; set; }
}
No exemplo a seguir, Dalek
é registrado como um valor em cascata usando CascadingValueSource<T>
, em que o tipo é <T>
. O sinalizador isFixed
indica se o valor é fixo. Se false
, todos os destinatários são inscritos para notificações de atualização. As assinaturas criam sobrecarga e reduzem o desempenho, portanto, defina isFixed
como true
se o valor não for alterado.
builder.Services.AddCascadingValue(sp =>
{
var dalek = new Dalek { Units = 789 };
var source = new CascadingValueSource<Dalek>(dalek, isFixed: false);
return source;
});
Aviso
Registrar um tipo de componente como um valor em cascata de nível raiz não registra serviços adicionais para o tipo nem permite a ativação do serviço no componente.
Trate os serviços necessários separadamente dos valores em cascata, registrando-os separadamente do tipo em cascata.
Evite usar AddCascadingValue para registrar um tipo de componente como um valor em cascata. Em vez disso, encapsule o <Router>...</Router>
no componente Routes
(Components/Routes.razor
) com o componente e adote a renderização interativa global do lado do servidor (SSR interativa). Para obter um exemplo, consulte a seção CascadingValue
componente.
Valores em cascata de nível raiz com notificações
A chamada NotifyChangedAsync para emitir notificações de atualização pode ser usada para sinalizar a vários componentes assinantes Razor que um valor em cascata foi alterado. As notificações não são possíveis para assinantes que adotam a renderização estática do lado do servidor (SSR estática), portanto, os assinantes devem adotar um modo de renderização interativo.
No exemplo a seguir:
-
NotifyingDalek
implementa INotifyPropertyChanged para notificar os clientes de que o valor de uma propriedade foi alterado. Quando aUnits
propriedade é definida, o PropertyChangedEventHandler (PropertyChanged
) é invocado. - O
SetUnitsToOneThousandAsync
método pode ser disparado pelos assinantes para definirUnits
como 1.000 com um atraso de processamento simulado.
Tenha em mente, para o código de produção, que qualquer alteração no estado (qualquer alteração no valor de uma propriedade da classe) faz com que todos os componentes inscritos sejam rerenderizados, independentemente de qual parte do estado eles utilizem. É recomendável criar classes granulares e vinculá-las separadamente com assinaturas específicas para garantir que apenas os componentes vinculados a uma porção específica do estado do aplicativo sejam afetados pelas alterações.
Observação
Para uma Blazor Web App solução que consiste em projetos de servidor e cliente (.Client
), o seguinte arquivo NotifyingDalek.cs
é colocado no projeto .Client
.
NotifyingDalek.cs
:
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class NotifyingDalek : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private int units;
public int Units
{
get => units;
set
{
if (units != value)
{
units = value;
OnPropertyChanged();
}
}
}
protected virtual void OnPropertyChanged(
[CallerMemberName] string? propertyName = default)
=> PropertyChanged?.Invoke(this, new(propertyName));
public async Task SetUnitsToOneThousandAsync()
{
// Simulate a three second delay in processing
await Task.Delay(3000);
Units = 1000;
}
}
O seguinte CascadingStateServiceCollectionExtensions
cria um CascadingValueSource<TValue> a partir de um tipo que implementa INotifyPropertyChanged.
Observação
Para uma Blazor Web App solução que consiste em projetos de servidor e cliente (.Client
), o seguinte arquivo CascadingStateServiceCollectionExtensions.cs
é colocado no projeto .Client
.
CascadingStateServiceCollectionExtensions.cs
:
using System.ComponentModel;
using Microsoft.AspNetCore.Components;
namespace Microsoft.Extensions.DependencyInjection;
public static class CascadingStateServiceCollectionExtensions
{
public static IServiceCollection AddNotifyingCascadingValue<T>(
this IServiceCollection services, T state, bool isFixed = false)
where T : INotifyPropertyChanged
{
return services.AddCascadingValue<T>(sp =>
{
return new CascadingStateValueSource<T>(state, isFixed);
});
}
private sealed class CascadingStateValueSource<T>
: CascadingValueSource<T>, IDisposable where T : INotifyPropertyChanged
{
private readonly T state;
private readonly CascadingValueSource<T> source;
public CascadingStateValueSource(T state, bool isFixed = false)
: base(state, isFixed = false)
{
this.state = state;
source = new CascadingValueSource<T>(state, isFixed);
this.state.PropertyChanged += HandlePropertyChanged;
}
private void HandlePropertyChanged(object? sender, PropertyChangedEventArgs e)
{
_ = NotifyChangedAsync();
}
public void Dispose()
{
state.PropertyChanged -= HandlePropertyChanged;
}
}
}
O tipo PropertyChangedEventHandler (HandlePropertyChanged
) chama o método CascadingValueSource<TValue> do NotifyChangedAsync para notificar os assinantes de que o valor em cascata foi alterado. O Task é descartado ao chamar NotifyChangedAsync pois a chamada representa apenas a duração do envio para o contexto síncrono. As exceções são tratadas internamente, enviando-as para o renderizador dentro do contexto do componente que gerou a exceção ao receber a atualização. Esta é a mesma forma como as exceções são processadas com um CascadingValue<TValue>, que não recebe notificação sobre exceções que ocorrem dentro dos destinatários de notificação. O manipulador de eventos é desconectado no Dispose
método para evitar um vazamento de memória.
No arquivo Program
, NotifyingDalek
é passado para criar um CascadingValueSource<TValue> com um valor inicial Unit
de 888 unidades:
builder.Services.AddNotifyingCascadingValue(new NotifyingDalek() { Units = 888 });
Observação
Para uma solução de Blazor Web App que consiste em projetos de servidor e cliente (.Client
), coloca-se o código anterior no arquivo Program
de cada projeto.
O componente a seguir é usado para demonstrar como alterar o valor de NotifyingDalek.Units
notifica os assinantes.
Daleks.razor
:
<h2>Daleks component</h2>
<div>
<b>Dalek Units:</b> @Dalek?.Units
</div>
<div>
<label>
<span style="font-weight:bold">New Unit Count:</span>
<input @bind="dalekCount" />
</label>
<button @onclick="Update">Update</button>
</div>
<div>
<button @onclick="SetOneThousandUnits">Set Units to 1,000</button>
</div>
<p>
Dalek© <a href="https://www.imdb.com/name/nm0622334/">Terry Nation</a><br>
Doctor Who© <a href="https://www.bbc.co.uk/programmes/b006q2x0">BBC</a>
</p>
@code {
private int dalekCount;
[CascadingParameter]
public NotifyingDalek? Dalek { get; set; }
private void Update()
{
if (Dalek is not null)
{
Dalek.Units = dalekCount;
dalekCount = 0;
}
}
private async Task SetOneThousandUnits()
{
if (Dalek is not null)
{
await Dalek.SetUnitsToOneThousandAsync();
}
}
}
Para demonstrar várias notificações de assinante, o componente a seguir DaleksMain
renderiza três Daleks
componentes. Quando a contagem de unidades (Units
) de um Dalek
componente é atualizada, os outros dois assinantes do Dalek
componente são atualizados.
DaleksMain.razor
:
@page "/daleks-main"
<PageTitle>Daleks Main</PageTitle>
<h1>Daleks Main</h1>
<Daleks />
<Daleks />
<Daleks />
Adicione um link de navegação ao componente DaleksMain
em NavMenu.razor
.
<div class="nav-item px-3">
<NavLink class="nav-link" href="daleks-main">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Daleks
</NavLink>
</div>
Como o tipo CascadingValueSource<TValue> neste exemplo (NotifyingDalek
) é do tipo classe, você pode atender praticamente a qualquer requisito de especificação de funcionalidades de gerenciamento de estado. No entanto, as assinaturas criam sobrecarga e reduzem o desempenho, portanto, comparam o desempenho dessa abordagem em seu aplicativo e a comparam com outras abordagens de gerenciamento de estado antes de adotá-la em um aplicativo de produção com recursos de processamento e memória restritos.
Qualquer alteração no estado (qualquer alteração de valor de propriedade da classe) faz com que todos os componentes inscritos sejam rerenderizados, independentemente de qual parte do estado eles usam. Evite criar uma única classe grande que represente todo o estado do aplicativo global. Em vez disso, crie classes granulares e as faça de forma independente com assinaturas específicas para parâmetros de cascata, garantindo que apenas os componentes inscritos em uma parte específica do estado do aplicativo sejam afetados pelas alterações.
componente CascadingValue
Um componente ancestral fornece um valor em cascata usando o componente Blazor da estrutura do CascadingValue
, que encapsula uma subárvore de uma hierarquia de componentes e fornece um único valor para todos os componentes dentro de sua subárvore.
O exemplo a seguir demonstra o fluxo de informações de tema na hierarquia de componentes para fornecer uma classe de estilo CSS a botões em componentes filho.
A classe C# ThemeInfo
a seguir especifica as informações do tema.
Observação
Para os exemplos nesta seção, o namespace do aplicativo será BlazorSample
. Ao testar o código em seu próprio aplicativo de exemplo, troque o namespace do aplicativo pelo namespace do aplicativo de exemplo.
ThemeInfo.cs
:
namespace BlazorSample;
public class ThemeInfo
{
public string? ButtonClass { get; set; }
}
namespace BlazorSample;
public class ThemeInfo
{
public string? ButtonClass { get; set; }
}
namespace BlazorSample.UIThemeClasses;
public class ThemeInfo
{
public string? ButtonClass { get; set; }
}
namespace BlazorSample.UIThemeClasses;
public class ThemeInfo
{
public string? ButtonClass { get; set; }
}
namespace BlazorSample.UIThemeClasses
{
public class ThemeInfo
{
public string ButtonClass { get; set; }
}
}
namespace BlazorSample.UIThemeClasses
{
public class ThemeInfo
{
public string ButtonClass { get; set; }
}
}
O componente de layout a seguir especifica informações de tema (ThemeInfo
) como um valor em cascata para todos os componentes que compõem o corpo do layout da propriedade Body. Um valor de ButtonClass
, que é um estilo de botão Bootstrap, é atribuído a btn-success
. Qualquer componente descendente na hierarquia de componentes pode usar a propriedade ButtonClass
por meio do valor em cascata ThemeInfo
.
MainLayout.razor
:
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div>
<CascadingValue Value="theme">
<article class="content px-4">
@Body
</article>
</CascadingValue>
</main>
</div>
<div id="blazor-error-ui" data-nosnippet>
An unhandled error has occurred.
<a href="." class="reload">Reload</a>
<span class="dismiss">🗙</span>
</div>
@code {
private ThemeInfo theme = new() { ButtonClass = "btn-success" };
}
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div>
<CascadingValue Value="theme">
<article class="content px-4">
@Body
</article>
</CascadingValue>
</main>
</div>
<div id="blazor-error-ui" data-nosnippet>
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
@code {
private ThemeInfo theme = new() { ButtonClass = "btn-success" };
}
@inherits LayoutComponentBase
@using BlazorSample.UIThemeClasses
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>
<CascadingValue Value="@theme">
<article class="content px-4">
@Body
</article>
</CascadingValue>
</main>
</div>
@code {
private ThemeInfo theme = new() { ButtonClass = "btn-success" };
}
@inherits LayoutComponentBase
@using BlazorSample.UIThemeClasses
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<CascadingValue Value="@theme">
<div class="content px-4">
@Body
</div>
</CascadingValue>
</main>
</div>
@code {
private ThemeInfo theme = new() { ButtonClass = "btn-success" };
}
@inherits LayoutComponentBase
@using BlazorSample.UIThemeClasses
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<CascadingValue Value="@theme">
<div class="content px-4">
@Body
</div>
</CascadingValue>
</div>
</div>
@code {
private ThemeInfo theme = new() { ButtonClass = "btn-success" };
}
@inherits LayoutComponentBase
@using BlazorSample.UIThemeClasses
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<CascadingValue Value="theme">
<div class="content px-4">
@Body
</div>
</CascadingValue>
</div>
@code {
private ThemeInfo theme = new ThemeInfo { ButtonClass = "btn-success" };
}
Os Blazor Web Apps fornecem abordagens alternativas para valores em cascata que se aplicam de forma mais ampla ao aplicativo do que fornecê-los por meio de um arquivo de layout:
Encapsule a marcação do componente
Routes
em um componenteCascadingValue
para especificar os dados como um valor em cascata para todos os componentes do aplicativo.O exemplo a seguir deriva dados
ThemeInfo
em cascata a partir do componenteRoutes
.Routes.razor
:<CascadingValue Value="theme"> <Router ...> ... </Router> </CascadingValue> @code { private ThemeInfo theme = new() { ButtonClass = "btn-success" }; }
Observação
Não há suporte para encapsular a instância do componente
Routes
no componenteApp
(Components/App.razor
) com um componenteCascadingValue
.Especifique um valor em cascata de nível raiz como um serviço chamando o método de extensão AddCascadingValue no construtor de coleção de serviços.
O exemplo a seguir deriva dados
ThemeInfo
em cascata a partir do componenteProgram
.Program.cs
builder.Services.AddCascadingValue(sp => new ThemeInfo() { ButtonClass = "btn-primary" });
Para obter mais informações, veja as seguintes seções deste artigo:
Atributo [CascadingParameter]
Para usar valores em cascata, os componentes descendentes declaram parâmetros em cascata usando o atributo [CascadingParameter]
. Os valores em cascata estão associados a parâmetros em cascata por tipo. Vários valores em cascata do mesmo tipo são abordados na seção Cascata de vários valores posteriormente neste artigo.
O componente a seguir associa o valor em cascata ThemeInfo
a um parâmetro em cascata, opcionalmente usando o mesmo nome de ThemeInfo
. O parâmetro é usado para definir a classe CSS para o botão Increment Counter (Themed)
.
ThemedCounter.razor
:
@page "/themed-counter"
<PageTitle>Themed Counter</PageTitle>
<h1>Themed Counter Example</h1>
<p>Current count: @currentCount</p>
<p>
<button @onclick="IncrementCount">
Increment Counter (Unthemed)
</button>
</p>
<p>
<button
class="btn @(ThemeInfo is not null ? ThemeInfo.ButtonClass : string.Empty)"
@onclick="IncrementCount">
Increment Counter (Themed)
</button>
</p>
@code {
private int currentCount = 0;
[CascadingParameter]
protected ThemeInfo? ThemeInfo { get; set; }
private void IncrementCount() => currentCount++;
}
@page "/themed-counter"
<PageTitle>Themed Counter</PageTitle>
<h1>Themed Counter Example</h1>
<p>Current count: @currentCount</p>
<p>
<button @onclick="IncrementCount">
Increment Counter (Unthemed)
</button>
</p>
<p>
<button
class="btn @(ThemeInfo is not null ? ThemeInfo.ButtonClass : string.Empty)"
@onclick="IncrementCount">
Increment Counter (Themed)
</button>
</p>
@code {
private int currentCount = 0;
[CascadingParameter]
protected ThemeInfo? ThemeInfo { get; set; }
private void IncrementCount() => currentCount++;
}
@page "/themed-counter"
@using BlazorSample.UIThemeClasses
<h1>Themed Counter</h1>
<p>Current count: @currentCount</p>
<p>
<button @onclick="IncrementCount">
Increment Counter (Unthemed)
</button>
</p>
<p>
<button
class="btn @(ThemeInfo is not null ? ThemeInfo.ButtonClass : string.Empty)"
@onclick="IncrementCount">
Increment Counter (Themed)
</button>
</p>
@code {
private int currentCount = 0;
[CascadingParameter]
protected ThemeInfo? ThemeInfo { get; set; }
private void IncrementCount()
{
currentCount++;
}
}
@page "/themed-counter"
@using BlazorSample.UIThemeClasses
<h1>Themed Counter</h1>
<p>Current count: @currentCount</p>
<p>
<button @onclick="IncrementCount">
Increment Counter (Unthemed)
</button>
</p>
<p>
<button
class="btn @(ThemeInfo is not null ? ThemeInfo.ButtonClass : string.Empty)"
@onclick="IncrementCount">
Increment Counter (Themed)
</button>
</p>
@code {
private int currentCount = 0;
[CascadingParameter]
protected ThemeInfo? ThemeInfo { get; set; }
private void IncrementCount()
{
currentCount++;
}
}
@page "/themed-counter"
@using BlazorSample.UIThemeClasses
<h1>Themed Counter</h1>
<p>Current count: @currentCount</p>
<p>
<button @onclick="IncrementCount">
Increment Counter (Unthemed)
</button>
</p>
<p>
<button class="btn @ThemeInfo.ButtonClass" @onclick="IncrementCount">
Increment Counter (Themed)
</button>
</p>
@code {
private int currentCount = 0;
[CascadingParameter]
protected ThemeInfo ThemeInfo { get; set; }
private void IncrementCount()
{
currentCount++;
}
}
@page "/themed-counter"
@using BlazorSample.UIThemeClasses
<h1>Themed Counter</h1>
<p>Current count: @currentCount</p>
<p>
<button @onclick="IncrementCount">
Increment Counter (Unthemed)
</button>
</p>
<p>
<button class="btn @ThemeInfo.ButtonClass" @onclick="IncrementCount">
Increment Counter (Themed)
</button>
</p>
@code {
private int currentCount = 0;
[CascadingParameter]
protected ThemeInfo ThemeInfo { get; set; }
private void IncrementCount()
{
currentCount++;
}
}
De modo semelhante a um parâmetro de componente regular, os componentes que aceitam um parâmetro em cascata serão gerados novamente quando o valor em cascata for alterado. Por exemplo, a configuração de uma instância de tema diferente faz com que o componente ThemedCounter
da seção componente CascadingValue
seja renderizado.
MainLayout.razor
:
<main>
<div class="top-row px-4">
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>
<CascadingValue Value="theme">
<article class="content px-4">
@Body
</article>
</CascadingValue>
<button @onclick="ChangeToDarkTheme">Dark mode</button>
</main>
@code {
private ThemeInfo theme = new() { ButtonClass = "btn-success" };
private void ChangeToDarkTheme()
{
theme = new() { ButtonClass = "btn-secondary" };
}
}
CascadingValue<TValue>.IsFixed pode ser usado para indicar que um parâmetro em cascata não será alterado após a inicialização.
Valores/parâmetros em cascata e limites do modo de renderização
Os parâmetros em cascata não passam dados entre os limites do modo de renderização:
As sessões interativas são executadas em um contexto diferente das páginas que usam a renderização estática (SSR estática). Não há nenhum requisito de que o servidor que produz a página seja mesmo o mesmo computador que hospeda algumas sessões posteriores do Servidor Interativo, incluindo para componentes WebAssembly em que o servidor é um computador diferente para o cliente. O benefício da renderização estática do lado do servidor (SSR estática) é obter o desempenho total da renderização HTML sem estado pura.
O estado que cruza o limite entre a renderização estática e interativa deve ser serializável. Os componentes são objetos arbitrários que fazem referência a uma vasta cadeia de outros objetos, incluindo o renderizador, o contêiner de DI e cada instância de serviço de DI. Você deve explicitamente serializar o estado a partir da SSR estática para disponibilizá-lo nos componentes subsequentes renderizados interativamente. Duas abordagens são adotadas:
- Por meio da estrutura Blazor, os parâmetros passados por uma SSR estática para um limite de renderização interativa são automaticamente serializados se forem serializados por JSON, ou um erro é gerado.
- O estado armazenado no
PersistentComponentState
será serializado e recuperado automaticamente se for serializado por JSON, ou um erro será gerado.
Parâmetros em cascata não são serializados por JSON porque os padrões de uso típicos para parâmetros em cascata são um pouco parecidos com os serviços de DI. Geralmente, há variantes específicas da plataforma de parâmetros em cascata, portanto, seria inútil para os desenvolvedores se a estrutura impedisse os desenvolvedores de ter versões específicas interativas do servidor ou versões específicas do WebAssembly. Além disso, muitos valores de parâmetro em cascata em geral não são serializáveis, portanto, seria impraticável atualizar aplicativos existentes se você tivesse que parar de usar todos os valores de parâmetro em cascata não serializáveis.
Recomendações:
Se você precisar disponibilizar o estado para todos os componentes interativos como um parâmetro em cascata, recomendamos usar valores em cascata no nível raiz ou valores em cascata no nível raiz com notificações. Há um padrão de fábrica disponível e o aplicativo pode emitir valores atualizados após sua inicialização. Os valores em cascata de nível raiz estão disponíveis para todos os componentes, incluindo componentes interativos, pois são processados como serviços de DI.
Para autores da biblioteca de componentes, você pode criar um método de extensão para consumidores de biblioteca semelhante ao seguinte:
builder.Services.AddLibraryCascadingParameters();
Instrua os desenvolvedores a chamar seu método de extensão. Essa é uma boa alternativa para instruí-los a adicionar um componente
<RootComponent>
em seus componentesMainLayout
.
Valores múltiplos em cascata
Para colocar em cascata vários valores do mesmo tipo na mesma subárvore, forneça uma cadeia de caracteres Name exclusiva para cada componente CascadingValue
e seus atributos [CascadingParameter]
correspondentes.
No exemplo a seguir, dois componentes CascadingValue
colocam em cascata instâncias diferentes de CascadingType
:
<CascadingValue Value="parentCascadeParameter1" Name="CascadeParam1">
<CascadingValue Value="ParentCascadeParameter2" Name="CascadeParam2">
...
</CascadingValue>
</CascadingValue>
@code {
private CascadingType? parentCascadeParameter1;
[Parameter]
public CascadingType? ParentCascadeParameter2 { get; set; }
}
Em um componente descendente, os parâmetros em cascata recebem seus valores em cascata do componente ancestral por Name:
@code {
[CascadingParameter(Name = "CascadeParam1")]
protected CascadingType? ChildCascadeParameter1 { get; set; }
[CascadingParameter(Name = "CascadeParam2")]
protected CascadingType? ChildCascadeParameter2 { get; set; }
}
Passar dados em uma hierarquia de componentes
Os parâmetros em cascata também permitem que os componentes passem dados por uma hierarquia de componentes. Leve em consideração o exemplo de conjunto de guias da interface do usuário a seguir, em que um componente de conjunto de guias mantém uma série de guias individuais.
Observação
Para os exemplos nesta seção, o namespace do aplicativo será BlazorSample
. Ao testar o código em seu próprio aplicativo de exemplo, troque o namespace pelo namespace do aplicativo de exemplo.
Crie uma interface ITab
que as guias implementem em uma pasta chamada UIInterfaces
.
UIInterfaces/ITab.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.UIInterfaces;
public interface ITab
{
RenderFragment ChildContent { get; }
}
Observação
Para obter mais informações sobre RenderFragment, confira Componentes Razordo ASP.NET Core.
O componente TabSet
a seguir mantém um conjunto de guias. Os componentes Tab
do conjunto de guias, que serão criados posteriormente nesta seção, fornecem os itens de lista (<li>...</li>
) para a lista (<ul>...</ul>
).
Os componentes Tab
filho não são explicitamente passados como parâmetros para TabSet
. Em vez disso, os componentes Tab
filho fazem parte do conteúdo filho de TabSet
. No entanto, o TabSet
ainda precisa de uma referência a cada componente Tab
para que ele possa renderizar os cabeçalhos e a guia ativa. Para habilitar essa coordenação sem exigir código adicional, o componente TabSet
pode fornecer-se como um valor em cascata que, em seguida, é captado pelos componentes de Tab
descendentes.
TabSet.razor
:
@using BlazorSample.UIInterfaces
<!-- Display the tab headers -->
<CascadingValue Value="this">
<ul class="nav nav-tabs">
@ChildContent
</ul>
</CascadingValue>
<!-- Display body for only the active tab -->
<div class="nav-tabs-body p-4">
@ActiveTab?.ChildContent
</div>
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
public ITab? ActiveTab { get; private set; }
public void AddTab(ITab tab)
{
if (ActiveTab is null)
{
SetActiveTab(tab);
}
}
public void SetActiveTab(ITab tab)
{
if (ActiveTab != tab)
{
ActiveTab = tab;
StateHasChanged();
}
}
}
Os componentes Tab
descendentes capturam o TabSet
presente como um parâmetro em cascata. Os componentes Tab
se adicionam à TabSet
e se coordenam e para definir a guia ativa.
Tab.razor
:
@using BlazorSample.UIInterfaces
@implements ITab
<li>
<a @onclick="ActivateTab" class="nav-link @TitleCssClass" role="button">
@Title
</a>
</li>
@code {
[CascadingParameter]
public TabSet? ContainerTabSet { get; set; }
[Parameter]
public string? Title { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
private string? TitleCssClass =>
ContainerTabSet?.ActiveTab == this ? "active" : null;
protected override void OnInitialized()
{
ContainerTabSet?.AddTab(this);
}
private void ActivateTab()
{
ContainerTabSet?.SetActiveTab(this);
}
}
O componente ExampleTabSet
a seguir usa o componente TabSet
, que contém três componentes Tab
.
ExampleTabSet.razor
:
@page "/example-tab-set"
<TabSet>
<Tab Title="First tab">
<h4>Greetings from the first tab!</h4>
<label>
<input type="checkbox" @bind="showThirdTab" />
Toggle third tab
</label>
</Tab>
<Tab Title="Second tab">
<h4>Hello from the second tab!</h4>
</Tab>
@if (showThirdTab)
{
<Tab Title="Third tab">
<h4>Welcome to the disappearing third tab!</h4>
<p>Toggle this tab from the first tab.</p>
</Tab>
}
</TabSet>
@code {
private bool showThirdTab;
}