Diretivas estruturais de Angular e sua micro sintaxe

Diretivas estruturais de Angular e sua micro sintaxe

Você já se perguntou o que é aquele prefixo de asterisco antes de ngIf e ngFor? Isso é chamado diretiva estrutural.

Neste artigo eu vou te mostrar o que é, pra que serve e como funciona.

Também farei uma parte 2, mostrando como criar suas próprias diretivas estruturais.

LinkTemplates são a estrutura

Vamos começar definindo o que é.

Uma diretiva estrutural é uma diretiva com uma estrutura. A estrutura é um ng-template.

Quando você escreve <div><p>Text</p></div>, você está dizendo ao Angular para: “_Declarar a estrutura de uma div tag, com uma paragraph tag, com a string 'Text', e renderizá-la_”.

Mas quando você envolve o código em um <ng-template><div><p>Text</p></div></ng-template>, você está dizendo ao Angular para “_declarar a estrutura de uma tag 'div', com uma tag 'p', com a string 'Text'_”. Observe que agora não estamos dizendo ao Angular para renderizá-la.

Agora, coloque uma diretiva no <ng-template> e você terá uma diretiva estrutural: <ng-template [ngIf]=“condition”><div><p>Text</p></div></ng-template>

LinkSynthetic sugar

É assim que o ngIf funciona. O Angular analisa o <ng-template>, gerando uma TemplateRef, que então é injetada na diretiva NgIf. Se a condição passada em ngIf for verdadeira, o template é renderizado.

Mas seria muito chato criar um ng-template sempre que quisermos usar NgIf ou qualquer outra diretiva que requeira um ng-template. Então a equipe do Angular criou um açúcar sintático. É como um atalho.

Quando você prefixa sua diretiva com um asterisco, Angular envolve tudo em um &ng-template e aplica a diretiva ao ng-template. Portanto, <div *ngIf=“condition”>Abc</div>, torna-se um <ng-template [ngIf]=“condition”><div>Abc</div></ng-template>

É apenas açúcar sintático. Você pode escrever o seu aplicativo inteiro sem usar o asterisco se você quisesse.

LinkApenas um permitido

Sabendo como funciona, agora você vai entender porque só podemos usar uma diretiva estrutural por elemento. Se você usasse *ngIf e *ngFor no mesmo elemento como o Angular interpretaria isso? ngIf primeiro e depois ngFor? O contrário? Ambos no mesmo template?

LinkMicro sintaxe

Falando sobre ngFor, ele parece muito mais complicado do que ngIf, certo? Eu já vi algumas expressões bem complexas com ngFor, como passar uma função trackBy, passar um observable de array por um pipe, pegar o índice e verificar se é o último elemento.

TypeScript
<div *ngFor="let item of list$ | async; trackBy: trackByFn; let itemIndex = index; let isLast = last">{{ item }}</div>

Inicialmente, pensei que fosse um jargão específico do ngFor, mas não é. É uma sintaxe totalmente documentada que funciona para quaisquer diretivas estruturais, até mesmo diretivas que você mesmo crie. É chamada "micro sintaxe da diretiva estrutural". (meio óbvio)

A micro sintaxe da diretiva estrutural divide as expressões por ponto e vírgula (;). Em nosso exemplo com NgFor, teríamos 4 expressões:

  1. let item of list$ | async
  2. trackBy: trackByFn
  3. let itemIndex = index
  4. let isLast = last

LinkDeclarations

Expressões que começam com "let" são declarações de variáveis. Você declara o nome da variável logo após o "let" e usa o sinal de igual (=) para apontar para o nome da variável no contexto da diretiva exportada.

Eu sei, foi muita informação.

O que quero dizer é que, quando renderizamos um ng-template, podemos opcionalmente passar um objeto de contexto. E as propriedades deste objeto de contexto são passadas para o template. O objeto de contexto pode ter várias variáveis explícitas e uma única variável implícita.

TypeScript
<!-- Rendering an <ng-template> with a context object -->
<ng-container *ngTemplateOutlet="templateExample; context: { $implicit: 'test', index: 1 }"></ng-container>

<!-- Using the context properties in the <ng-template> -->
<ng-template #templateExample let-itemIndex="index" let-item>
  <p>#{{ itemIndex }} - {{ item }}</p>
</ng-template>

É como uma função em JavaScript, nós temos os parâmetros, que nós mesmo declaramos e que, portanto, são muito explícitos, e temos também this, que é uma variável implícita que existe mesmo que não a tenhamos declarado.

TypeScript
function example(itemIndex, isLast) {
  // Explicit
  console.log(itemIndex, isLast);

  // Implicit
  console.log(this);
}

Em uma função, você pode ter quantos parâmetros quiser, mas apenas um this. Da mesma forma, em um ng-template, você pode ter quantas variáveis explícitas quiser, mas apenas uma variável implícita.

A variável implícita é o que você obtém quando não aponta para nenhuma variável exportada. let item, por exemplo, obtém a variável implícita. Mas let isLast = last obtém a variável explícita last e let itemIndex = index obtém a variável explícita index.

Após interpretar os açúcares sintáticos, é isso que obtemos:

TypeScript
<ng-template let-item let-itemIndex="index" let-isLast="last">
    <p>#{{ itemIndex }} - {{ item }}</p>
    <p *ngIf="isLast">The end</p>
</ng-template>

LinkExpressões-chave

Expressões com dois argumentos e opcionalmente dois-pontos (:) entre eles são expressões-chave. A expressão (à direita) é atribuída à chave (à esquerda) com um prefixo antes.

Vejamos alguns exemplos.

Em \*ngIf="condition; else otherTemplate, na expressão else otherTemplate:

  • ngIf é o prefixo
  • else é a chave
  • otherTemplate é a expressão.

Isso é interpretado como <ng-template [ngIfElse]="otherTemplate"></ng-template>

Em *ngFor="let item of list; trackBy: trackByFn, na expressão trackBy: trackByFn:

  • ngFor é o prefixo
  • trackBy é a chave
  • trackByFn é a expressão.

Isso é interpretado como <ng-template [ngForTrackBy]="trackByFn"></ng-template>

Além disso, nesse exemplo com NgFor, of list em let item of list também é uma expressão chave.

  • ngFor é o prefixo
  • of é a chave
  • list é a expressão.

Isso é interpretado como <ng-template [ngForOf]="list"></ng-template>

LinkLocal bindings

A última coisa a mencionar é a palavra-chave opcional as no final da expressão. Ela declara uma variável do template e mapeia o resultado da expressão para ela.

*ngIf="condition as value" é interpretado como <ng-template [ngIf]="condition" let-value="ngIf">

LinkConclusão

É isso. Agora você entende como as diretivas estruturais funcionam e como analisar sua micro sintaxe.

Farei outro artigo sobre como criar uma diretiva estrutural do zero e como fazer com que o compilador do Angular verifique o contexto dela.

Até lá, tenha um ótimo dia e nos vemos em breve!

LinkReferências

  1. Documentação diretivas estruturais Angular docs
  2. Implementação da diretiva ngIf GitHub

Assine a nossa Newsletter e seja avisado quando eu lançar um curso, postar um vídeo ou escrever um artigo.

Campo obrigatório
Campo obrigatório