Angular: Diretivas
Material completo: Iniciando os estudos com Angular
As diretivas no Angular, são instruções/marcadores que aplicam um comportamento em nossa aplicação.
- Diretivas Estruturais: São diretivas que modificam diretamente a nossa DOM, como o caso do *ngFor e *ngIf, para renderizar uma lista de elementos ou controlar se irá ser exibido em tela ou não.
- Diretivas de Atributos: São diretivas para alterar o comportamento de algum elemento, mexendo diretamente na sua classe ou estilo, como a ng-class e ng-style.
Diretiva: *ngIf x Hidden
A diretiva *ngIf, serve para criarmos condicionais em nosso template, assim como o if/else presente nas linguagens de programação e controlar qual componente/elemento será exibido em tela de acordo com a condição ou propriedade passada.
Segurança: Talvez, somente um usuário logado ou com determinada permissão, deva enxergar o conteúdo, por isso, pode não ser recomendado utilizar a propriedade hidden, pois ele só estará oculto em tela, mas ainda será visível na DOM.
Custo: Quando um elemento é mostrado ou ocultado em tela com o *ngIf, o Angular está realizando um processo de construção e destruição. Para códigos onde a árvore de elementos DOM é muito extensa, esse processo fica muito custoso para o browser. Por isso, deve ser feito uma analise de cenário para verificar onde irá utilizar hidden e onde utilizará *ngIf.
Diretivas: ngSwitch, ngSwitchCase e ngSwitchDefault
Também podemos utilizar o switch case para realizar condicionais e exibir elementos em tela. Caso tivermos 3 ou mais casos de exibição, podemos utiliza-lo no lugar no *ngIf.
Para esse caso, temos criado em nosso componente.ts, uma variável navbar=’home’. Estamos utilizando alguns conceitos de data-binding já aprendidos aqui, onde estamos aplicando a classe “active” de acordo com as ações de clique que realizamos no menu de navegação.
O que temos de novo até aqui, é o último bloco, onde estamos trabalhando com as diretivas ngSwitch, ngSwitchCase e ngSwitchDefault.
- [ngSwitch]=”navbar”: Encarregada de receber nossa variável com o valor atual;
- *ngSwitchCase: São os casos que temos, se coincidir do valor ser igual a que está no ngSwitch, é esse elemento que exibiremos em tela;
- *ngSwitchDefault: Se nenhum dos valores dos casos coincidir com o ngSwitch, esse elemento será exibido por padrão.
Diretiva: *ngFor
Essa é uma diretiva que nos permite iterar sobre uma lista de valores presentes em um array. Muito parecido com um for loop presente no JavaScript, como no exemplo abaixo.
for (var i = 0; i < 10; i++) {
// algum código
}Em nosso component.ts, declaramos uma variável recebendo um array de strings:
cursos: string[] = [‘Angular’, ‘React’, ‘Vue’];Em nosso template.html, criamos uma lista não ordenada, onde seus itens serão renderizados através de uma iteração feita no array cursos.
Para isso, utilizaremos a diretiva *ngFor, que irá percorrer o array cursos e para cada iteração, irá salvar o valor atual na variável curso.
Apenas como exemplo, também estamos salvando o valor atual do index, para ser mostrado em tela.
Após realizar todas as iterações, esse é o resultado final, renderizando todos os elementos do array, e exibindo seu index (index+1).
Diretiva: ngClass
A diretiva ngClass, permite aplicar classes ao elemento de acordo com a condição passada. Já vimos como fazer isso utilizando class-binding, mas dependendo da quantidade de classes, seu código pode ficar ilegível.
Abaixo, temos os dois exemplos de como aplicar classes, utilizando a diretiva [ngClass], podemos passar um objeto, onde a string passada será o nome da classe a ser aplicada, e o valor é um booleano.
Podemos visualizar que o resultado final é o mesmo, cabe o desenvolvedor escolher a melhor alternativa. Lembrando que caso esteja aplicando muitas classes, talvez seja melhor utilizar a diretiva ngClass.
Diretiva: ngStyle
A diretiva ngStyle também é muito parecido com a que vimos anteriormente, mas ao invés de aplicarmos classes, podemos trabalhar diretamente com estilos CSS.
Para isso, podemos utilizar [ngStyle] e passar um objeto com todas as propriedades do CSS que queremos alterar. Lembre-se que estamos trabalhando com JavaScript, então as propriedades são camelCase.
Abaixo, temos o component.ts com a propriedade e método que estamos utilizando para executar.
Feito! Temos o mesmo funcionamento do style-binding, mas utilizando o ngStyle e deixando nosso código mais organizado e legível.
Operador Elvis (?)
Muitas das vezes, quando estamos desenvolvendo e por mais que tomemos cuidado, iremos se deparar com situações parecidas com a abaixo, onde queremos exibir alguma informação em tela, mas ela ainda é null/undefined e caímos nesse erro.
Nota: Os navegadores já não apresentam mais esse erro e entendem que caso o valor seja null, não deve ser exibido nada. Mas o uso do Operador Elvis ainda é muito útil para outros casos.
Para começarmos, em nosso component.ts, vamos criar uma variável que recebe um objeto simples, onde a propriedade “responsavel” é nula.
Temos duas formas de contornar o erro de leitura e não prejudicar a renderização dos nossos componentes em tela.
- Operador ternário: Fazendo uso do operador ternário, temos um se “tarefa.responsavel” for diferente de null, exiba o valor, caso contrario, mostre uma string vazia.
- Operador Elvis: Fazendo uso do ponto de interrogação (?), estamos especificando aquela propriedade é opcional e ela pode ou não conter um valor. Caso tenha, exiba, caso contrário mostre uma string vazia.
Projeção de conteúdo: ng-content
Já aprendemos a passar valores utilizando o @Input() para nossos componentes filhos, correto? Mas e se quisermos passar um conteúdo maior?
No primeiro exemplo abaixo, importamos nosso componente filho, é um card criado com bootstrap e que está recebendo um título e um conteúdo dinâmico.
O título, temos que receber através do binding @Input() onde já aprendemos como utilizar no artigo de Data-Binding.
Agora, iremos mexer no template.html do nosso componente filho, onde:
- Titulo: Passamos nosso título através de interpolação, variável essa que está no nosso componente-filho.ts;
- Conteúdo: Todo o conteúdo que foi passado entre as tags <app-exemplo-ng-content> será exibida dentro do <ng-content>.
Esse é o resultado do nosso card, com as informações de título e conteúdo sendo exibidas dinamicamente.
Mas e se eu quiser passar todo o conteúdo direto para o componente sem fazer uso de data-binding? É possível!
Abaixo, estamos passando o conteúdo e apenas criando marcações com classes, para que o ng-content consiga recuperar essas informações futuramente.
Agora, no template.html do nosso componente-filho, recuperamos as informações utilizando apenas o ng-content, mas perceba que ganhamos um atributo novo: select.
- Titulo: Resgatamos o titulo através do select:”.titulo” pegando o conteúdo da div que possuí essa classe;
- Conteúdo: É a mesma coisa, mas agora resgatamos o conteúdo através do select:”.corpo”.
Esse é o resultado final, o mesmo comportamento mas agora fazendo uso somente do ng-content.
Diretivas Customizadas utilizando: ElementRef e Renderer2
Até o momento, aprendemos a utilizar algumas diretivas que o próprio Angular nos fornece. Mas e se quisermos criar nossas próprias diretivas para realizar comportamentos específicos?
Para criar uma nova diretiva, utilize:
ng g d nome-diretivaCriaremos uma diretiva onde a única finalidade dela é aplicar ao elemento um estilo para deixa-lo com o fundo amarelo. Antes de começar a criar, precisamos injetar duas dependências (ElementRef e Renderer2) para acessar as propriedades do elemento.
Get Rafael Simionato Zorzenon’s stories in your inbox
Join Medium for free to get updates from this writer.
Abaixo, temos um exemplo de como aplicar o estilo de duas maneiras diferentes:
ElementRef: É uma forma de recuperar um objeto e acessar as propriedades de um elemento na DOM, podemos navegar até a propriedade de estilos e alterar nosso background.
this.elementRef.nativeElement.style.backgroundColor = 'yellow';Observação: Somente utilizar ElementRef é uma má prática e não é recomendado pelo Angular, pois pode abrir vulnerabilidades no código.
ElementRef + Renderer2: É a forma mais segura de acessarmos as propriedades do elemento e realizar mudanças.
O método renderer, espera receber 3 valores onde:
- É o elemento que irá receber o estilo;
- É a propriedade CSS que irá ser alterada (É recomendado utilizar da forma que escrevemos no arquivo.css, mas também aceita camelCase);
- O valor que o atributo irá receber;
this.renderer.setStyle(this.elementRef.nativeElement, 'background-color', 'yellow')Para utilizar, basta adicionar o seletor da diretiva nos elementos que devem receber o background amarelo.
Caso queira limitar essa diretiva para funcionar apenas em um elemento ou componente, basta adicionar um prefixo no seletor da diretiva.
Nesse caso, queremos que ela funcione apenas em elementos que seja um paragrafo <p>.
@Directive({
selector: 'p[appFundoAmarelo]'
})Mesmo aplicando a diretiva no botão e titulo, somente o elemento <p> recebeu o fundo amarelo.
Diretivas Customizadas utilizando: HostListener e HostBinding
Agora, vamos criar uma diretiva customizada que ficará escutando alguns eventos, para isso, vamos fazer uso do HostListener.
Primeiro, vamos criar nossa nova diretiva, utilizando o Angular CLI:
ng g d shared/highlight-mouseApós a criação, já podemos utiliza-la em nosso template.html, mas por enquanto nada irá acontecer.
Para que possamos ficar escutando os eventos, utilizaremos o @HostListener() como forma de decorator, onde recebe o evento que deve escutar e a função que será disparada.
Para que possamos aplicar alguma mudança no elemento html, também será necessário importar e usar o ElementRef e o Renderer2 como no tópico anterior.
Note que criamos duas funções, “mouseenter” e “mouseleave” e ambas são exatamente iguais, alterando apenas a cor do background e com a impressão que estamos repetindo código.
Existe uma segunda forma de construirmos essa diretiva que é utilizar @HostListener() e @HostBinding() em conjunto, sem a necessidade de injeção de dependências.
Primeiramente, criamos um binding, onde deve acessar a propriedade ‘style.backgroundColor’ do elemento, e gravamos em uma variável para ser utilizada posteriormente.
Agora, dentro das funções, apenas acessamos a variável criada e passamos qual a cor que esse estilo deverá receber. Com isso, nossa diretiva fica mais simples e enxuta.
E esse é o resultado final, qualquer forma que você escolher para fazer sua diretiva, o comportamento é o mesmo.
Diretivas Customizadas utilizando: Input e Property Binding
Vamos reaproveitar o mesmo código da diretiva customizada do exemplo anterior, mas agora será possível passar quais as cores queremos no highlight utilizando Input() e Property Binding.
Afim só de manter uma organização, vamos criar uma nova diretiva:
ng g d shared/highlightApós a criação da diretiva, já podemos em nosso template.html atribuir a diretiva (appHighlight) a algum elemento. Observe também que eu já estou passando duas propriedades com seus respectivos valores.
- appHighlight: É a tag da diretiva;
- DefaultColor: É a cor de fundo padrão que eu quero para o elemento;
- highlightColor: É a cor de fundo que eu quero ao passar o mouse no elemento;
Lembra que estamos passando dois valores para o nosso componente? Perfeito! Estamos recuperando os valores passados através do @Input(). Mas como a passagem de valor é opcional, também atribuimos valores padrões a elas.
O que mudou, é que antes passavamos valores fixos de “initial” e “yellow” para as funções, agora passamos através de váriaveis.
Apenas um problema, quando o componente renderizar, ele iniciará sem fundo, pois ele pega o valor ‘initial’. Para resolver esse problema, criamos um método de lifecycle ngOnInit(), assim, ao construir o elemento, ele já iniciará com a cor de background que definimos no template.html.
Esse é o resultado final, tendo cinza como background inicial e ao passar o mouse, trocamos para vermelho.
Diretivas Customizadas de Estrutura utilizando: TemplateRef e ViewContainerRef
Para finalizar esse módulo, vamos criar uma diretiva que irá controlar a estrutura do nosso componente exibindo ou não de acordo com a variável recebida, parecida com o *ngIf.
Primeiro, vamos criar nossa nova diretiva:
ng g d shared/diretiva-ngelseCom a diretiva criada, iremos precisar de algumas linhas de código que utilizamos no inicio desse artigo, no tópico Diretiva: *ngIf x Hidden. Basta deixar seu component.ts com o código abaixo.
Após a criação da nossa diretiva, podemos pegar o seletor gerado na etapa de criação (*appDiretivaNgelse) e aplicar em um elemento html.
A ideia, é criar um comportamento parecido com a diretiva *ngIf, mas com a execução contrária.
Precisamos importar e injetar como dependências dois modelos: TemplateRef e ViewContainerRef, tem como objetivo criar instancias para referenciar o template e o elemento respectivamente.
Como estamos recebendo uma propriedade com um valor booleano, precisamos receber através do @Input() e utilizando um setter (set) do TypeScript, vamos ficar observando mudanças no elemento.
A regra é simples, caso a variável passada seja false, criaremos um container e passaremos a referencia do nosso elemento que será criado utilizando o método createEmbeddedView. Caso seja true, o elemento seja destruído passando o método clear().
Observe que teremos um resultado parecido quando estávamos utilizando somente *ngIf, onde negamos a variável mostrarCursos quando não havia cursos a serem mostrados.
<div *ngIf="mostrarCursos">
Lista de cursos aqui:
</div>
<div *ngIf="!mostrarCursos">
Não existem cursos a serem listados.
</div>Utilizando agora a diretiva customizada que criamos, o comportamento se manteve o mesmo.
Documentação
- Angular: Diretivas
- Angular: Diretiva *ngIf
- Angular: Diretiva ngSwitch
- Angular: Diretiva *ngSwitchCase
- Angular: Diretiva ngSwitchDefault
- Angular: Diretiva *ngFor
- Angular: Diretiva ngClass
- Angular: Diretiva ngStyle
- Alura: Operador Elvis
- Angular: ng-content
- Angular: ElementRef
- Angular: Renderer2
- Angular: HostListener
- Angular: HostBinding
- Angular: TemplateRef
- Angular: ViewContainerRef










