Compartilhar via



Março de 2019

Volume 34 – Número 3

[Moderno]

Componentes hierárquicos do Blazor

Por Dino Esposito

Dino EspositoComo a estrutura mais recente para unir a parte do SPA (aplicativo de página única), o Blazor teve a oportunidade de criar as melhores características de outras estruturas, como Angular e React. Embora o principal conceito por trás do Blazor seja aproveitar o C# e o Razor para a criação de aplicativos SPA, um aspecto claramente inspirado por outras estruturas é o uso de componentes.

Os componentes do Blazor são escritos usando a linguagem Razor, a mesma forma que as exibições do MVC são criadas, e é aí que as coisas ficam mais interessantes para os desenvolvedores. No ASP.NET Core, você pode alcançar níveis sem precedentes de expressividade por meio de novos artefatos de linguagem chamados auxiliares de marcas. Um auxiliar de marca é uma classe de C# instruída para analisar uma árvore de marcação indicada para transformá-la em HTML5 válido. Todas as ramificações que você pode enfrentar durante a criação de um bloco complexo, sob medida, de HTML são manipuladas no código e tudo o que os desenvolvedores escrevem nos arquivos de texto é marcação simples. Com os auxiliares de marca, a quantidade de trechos de código diminui significativamente. Os auxiliares de marca são ótimos, mas ainda apresentam alguns problemas de programação que os componentes Blazor resolvem de forma brilhante. Neste artigo, criarei um novo componente do Blazor que apresenta uma caixa de diálogo modal por meio dos serviços da estrutura do Bootstrap 4. Dessa forma, vou lidar com componentes com modelo do Blazor e parâmetros em cascata.

Dobras dos Auxiliares de Marcas

No meu livro "Programming ASP.NET Core" (Microsoft Press, 2018), apresento um exemplo de auxiliar de marca que faz praticamente o mesmo trabalho discutido anteriormente. Ele transforma algumas marcações não HTML ad-hoc em marcação específica do Bootstrap para as caixas de diálogo modais (confira bit.ly/2RxmWJS).

Qualquer transformação entre a marcação de entrada e a saída desejada é realizada por meio de código C#. Um auxiliar de marca, na verdade, é uma classe C# simples que herda da classe de base TagHelper e substitui um único método. O problema é que a composição de marcação e transformação devem ser expressas no código. Embora isso acrescente muita flexibilidade, qualquer alteração também requer uma etapa de compilação. Em particular, você precisa usar o códio C# para descrever uma árvore DIV com todos os seus conjuntos de atributos e elementos filho.

No Blazor, as coisas vêm muito mais fáceis, pois você não precisa recorrer a auxiliares de marcas para criar uma sintaxe de marcação mais amigável para elementos sofisticados, como uma caixa de diálogo modal do Bootstrap. Vamos ver como criar um componente Modal no Blazor.

Caixas de diálogo modais

A ideia é configurar um componente reutilizável do Blazor que encapsule o componente de caixa de diálogo modal do Bootstrap. A Figura 1 apresenta a árvore de marcação HTML5 familiar necessária para o Bootstrap (versões 3. x e 4. x) funcionar.

Figura 1 A marcação do Bootstrap para caixas de diálogo modais

<button type="button" class="btn btn-primary"
        data-toggle="modal"
        data-target="#exampleModal">
  Open modal
</button>
<div class="modal">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">Modal title</h5>
        <button type="button" class="close" data-dismiss="modal">
          <span>&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <p>Modal body text goes here.</p>
      </div>
      <div class="modal-footer">
        <button type="button"
                class="btn btn-secondary"
                data-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
</div>

Nenhum desenvolvedor da Web tem o prazer de reiterar essa parte de marcações repetidamente em várias exibições e páginas. A maior parte da marcação é puro layout e as informações de variável só são o texto para exibição e talvez alguns estilos e botões. Aqui está uma marcação mais expressiva e fácil de se lembrar:

<Modal>
  <Toggle class="btn"> Open </Toggle>
  <Content>
    <HeaderTemplate> ... </HeaderTemplate>
    <BodyTemplate> ... </BodyTemplate>
    <FooterTemplate> ... </FooterTemplate>
  </Content>
</Modal>

Os elementos constituintes de um componente Modal ficam imediatamente visíveis no código de marcação mais expressiva. A marcação inclui um elemento wrapper Modal com duas subárvores filho: uma para o botão de alternância e uma para o conteúdo real.

De acordo com a sintaxe de modais do Bootstrap, qualquer caixa de diálogo deve ser um gatilho a ser exibido. Normalmente, o gatilho é um elemento de botão decorado com um par de atributos dados-alternância e dados-destino. O modal, no entanto, também pode ser disparado por meio do JavaScript. O subcomponente Alternância serve como o contêiner para a marcação de gatilho. O subcomponente Conteúdo, por sua vez, encapsula todo o conteúdo da caixa de diálogo e é dividido em três segmentos: corpo, cabeçalho e o rodapé.

Em resumo, com base no trecho de código anterior, a interface do usuário resultante é composta por um botão principal chamado "Abrir". Quando clicado, o botão mostra uma DIV preenchida com três camadas: corpo, cabeçalho e o rodapé.

Para criar os componentes aninhados necessários para a caixa de diálogo modal, você precisa lidar com componentes com modelo e parâmetros em cascata. Observe que os parâmetros em cascata exigem que você execute o Blazor 0.7.0 ou mais recente.

O Componente Modal

Vamos dar uma olhada no código exibido na Figura 2. A marcação é razoavelmente mínima e inclui um elemento DIV em torno de uma parte de marcação de modelo. O arquivo modal.cshtml na Figura 2 declara uma propriedade de modelo chamada ChildContent que coleta (obviamente, o suficiente) qualquer conteúdo filho. O resultado da marcação deve enviar por push um elemento DIV próximo que reúna a marcação de alternância e o conteúdo real a ser exibido na caixa de diálogo.

Figura 2 Código-fonte do componente Modal

<CascadingValue Value="@Context">
  <div>
    @ChildContent
  </div>
</CascadingValue>
@functions
{
  protected override void OnInit()
  {
    Context = new ModalContext
    {
      Id = Id,
      AutoClose = AutoClose
    };
  }
  ModalContext Context { get; set; }
  [Parameter] private string Id { get; set; }
  [Parameter] private bool AutoClose { get; set; }
  [Parameter] RenderFragment ChildContent { get; set; }
}

Aparentemente, esse componente do contêiner não é de grande utilidade. Contudo, ele desempenha uma função crucial dada à estrutura da marcação para caixas de diálogo do Bootstrap. Os componentes de botão de alternância e conteúdo compartilham a mesma ID que identifica exclusivamente a caixa de diálogo modal. Usando um componente wrapper, é possível capturar o valor de ID em apenas um lugar e colocá-lo em cascata abaixo da árvore. No entanto, nesse caso específico, a ID não é mesmo o único parâmetro que você deseja colocar em cascata por meio das camadas mais internas de marcação. Uma caixa de diálogo modal, opcionalmente, pode ter um botão Fechar no cabeçalho, bem como outros atributos relacionados ao tamanho da caixa de diálogo ou à animação. Todas essas informações podem ser agrupadas em um objeto de transferência de dados personalizado e colocadas em cascata através da árvore.

A classe ModalContext é usada para coletar a ID e o valor booliano para o botão de fechamento, conforme mostrado neste código:

public class ModalContext
{
  public string Id { get; set; }
  public bool AutoClose { get; set; }
}

O elemento CascadingValue captura a expressão fornecida e compartilha automaticamente com todos os componentes mais internos que associam diretamente a ele. Sem o recurso de parâmetros em cascata, qualquer valor compartilhado nos componentes complexos e hierárquicos deve ser injetado explicitamente sempre que necessário. Sem esse recurso, você teria que indicar a mesma ID duas vezes, conforme mostrado neste código:

<Modal>
  <Toggle id="myModal" class="btn btn-primary btn-lg">
    ...
  </Toggle>
  <Content id="myModal">
    ...
  </Content>
</Modal>

Os valores em cascata são úteis em situações em que o mesmo conjunto de valores deve ser passado ao longo da hierarquia de um componente complexo composto de vários subcomponentes. Observe que os valores em cascata devem ser agrupados em um único contêiner; portanto, se você precisar passar por vários valores escalares, defina primeiro um objeto de contêiner. A Figura 3 ilustra como os parâmetros fluem por meio da hierarquia dos componentes modais.

Valores em cascata nos componentes hierárquicos
Figura 3 Valores em cascata nos componentes hierárquicos

Dentro do componente Modal

O conteúdo interno do componente Modal é analisado recursivamente, e os componentes do botão de alternância e conteúdo cuidam disso. Exemplo de fonte do componente Toggle.cshtml:

<button class="@Class"
        data-toggle="modal"
        data-target="#@OutermostEnv.Id">
  @ChildContent
</button>
@functions
{
  [CascadingParameter] protected ModalContext OutermostEnv { get; set; }
  [Parameter] string Class { get; set; }
  [Parameter] RenderFragment ChildContent { get; set; }
}

Nessa implementação, o elemento de alternância é estilizado por meio de uma propriedade pública denominada Classe. O conteúdo do botão é capturado por meio de uma propriedade de modelo chamada ChildContent. Observe que no Blazor, uma propriedade de modelo chamada ChildContent automaticamente captura toda a marcação filho do elemento pai. Além disso, uma propriedade de modelo no Blazor é uma propriedade do tipo RenderFragment.

O interessante no código-fonte anterior é a ligação com os valores em cascata. Você pode usar o atributo CascadingParameter para decorar uma propriedade de componente, como OutermostEnv. Em seguida, a propriedade é preenchida com valores em cascata do nível mais interno. Como resultado, OutermostEnv usa o valor atribuído à instância do ModalContext recém-criado no método Init do componente raiz (confira a Figura 2).

No componente Alternância, o valor de Id em cascata é usado para definir o valor do atributo dados-destino. No jargão do Bootstrap, o atributo dados-destino de um botão de alternância da caixa de diálogo identifica a ID da DIV a ser exibida quando o botão de alternância é clicado.

O Conteúdo da Caixa de Diálogo Modal

Uma caixa de diálogo do Bootstrap é composta por até três blocos DIV dispostos verticalmente: cabeçalho, corpo e o rodapé. Todos eles são opcionais, mas deve-se ter pelo menos um definido para dar aos usuários alguns comentários mínimo. Um componente com modelo é o ajuste certo aqui. Exemplo de interface pública do componente Conteúdo resultante do arquivo Content.cshtml:

@functions
{
  [CascadingParameter] ModalContext OutermostEnv { get; set; }
  [Parameter] RenderFragment HeaderTemplate { get; set; }
  [Parameter] RenderFragment BodyTemplate { get; set; }
  [Parameter] RenderFragment FooterTemplate { get; set; }
}

O parâmetro em cascata de OutermostEnv trará os dados definidos fora do domínio do componente Conteúdo. As propriedades de ID e AutoClose são usadas aqui. O valor da Id é usado para identificar o contêiner mais externo da caixa de diálogo. A DIV assinada com a ID será exibida quando o modal for disparado. O valor AutoClose, por sua vez, é usado para controlar uma instrução IF que decide se um botão Descartar deve ou não ser colocado na barra de cabeçalho.

Por fim, três propriedades do modelo RenderFragment definem o conteúdo real para as áreas personalizáveis, isto é, cabeçalho, rodapé e o corpo.

Como mostrado na Figura 4, o componente Conteúdo faz a maior parte do trabalho para processar a marcação esperada do Bootstrap para as caixas de diálogo modais. Ele define o layout geral do HTML e usa as propriedades do modelo para importar os detalhes da marcação que fariam uma caixa de diálogo indicada exclusiva, ou seja, a marcação do cabeçalho, rodapé e corpo. Graças aos modelos Blazor, qualquer marcação real pode ser especificada como conteúdo embutido na página do chamador. Observe que o código-fonte da página do chamador (chamado de cascata no aplicativo de exemplo) é descrito na Figura 3.

Figura 4 Marcação do Componente Conteúdo

<div class="modal" id="@OutermostEnv.Id">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">
          @HeaderTemplate
        </h5>
        @if (OutermostEnv.AutoClose)
        {
          <button type="button" class="close"
                  data-dismiss="modal">
            <span>&times;</span>
          </button>
        }
      </div>
      <div class="modal-body">
        @BodyTemplate
      </div>
      <div class="modal-footer">
        @FooterTemplate
      </div>
    </div>
  </div>
</div>

Obter mais informações sobre parâmetros e valores em cascata

Os valores em cascata resolvem o problema no qual os valores fluem efetivamente para baixo da pilha de subcomponentes. Os valores em cascata podem ser definidos em vários níveis em uma hierarquia complexa e ir de um componente ancestral para todos os seus descendentes. Cada elemento ancestral pode definir um único valor em cascata, possivelmente, um objeto complexo que reúne vários valores escalares.

Para fazer uso dos valores em cascata, os componentes descendentes declaram parâmetros em cascata. Um parâmetro em cascata é uma propriedade pública ou protegida decorada com o atributo CascadingParameter. Os valores em cascata podem ser associados a uma propriedade Name, da seguinte forma:

<CascadingValue Value=@Context Name="ModalDialogGlobals">
  ...
</CascadingValue>

Nesse caso, os descendentes usarão a propriedade Name para recuperar o valor em cascata, conforme mostrado aqui:

[CascadingParameter(Name = "ModalDialogGlobals")]
ModalContext OutermostEnv { get; set; }

Quando nenhum nome for especificado, os valores em cascata serão vinculados por tipo aos parâmetros em cascata.

Conclusão

Os valores em cascata são projetados especificamente para componentes hierárquicos, mas ao mesmo tempo os componentes hierárquicos (e com modelos) são na verdade o tipo mais comum de componentes do Blazor que os desenvolvedores devem escrever. Este artigo demonstrou os parâmetros em cascata e os componentes hierárquicos e com modelos, mas também mostrou o quão poderoso poderia ser o uso dos componentes do Razor para expressar as partes específicas de marcação por meio de uma sintaxe de nível mais alto. Em particular, desenvolvi uma sintaxe de marcação personalizada para renderizar uma caixa de diálogo modal do Bootstrap. Observe que você pode ter o mesmo resultado no ASP.NET Core simples usando auxiliares de marcação ou auxiliares HTML no ASP.NET MVC clássico.

O código-fonte deste artigo está disponível em bit.ly/2FdGZat.


Dino Esposito é autor de mais de 20 livros e de mais de mil artigos em seus 25 anos de carreira. Autor de “The Sabbatical Break”, um show de estilo cênico, Esposito se ocupa escrevendo software para um mundo mais ecológico, como estrategista digital da BaxEnergy. Siga-o no Twitter: @despos.

Agradecemos aos seguintes especialistas técnicos da Microsoft pela revisão deste artigo: Daniel Roth