ngFor Angular - Como criar Poderosos Loops de Repetição

Angular

ngFor Angular - Como criar Poderosos Loops de Repetição

Gabriel Nascimento
Escrito por Gabriel Nascimento em 16 de abril de 2020

Sabe quando você vê postagens em uma rede social e elas aparecem uma atrás da outra?

Isso acontece com ajuda de um poderoso loop de repetição que percorre e cria todas as postagens, uma a uma… 

E se você também precisa criar múltiplos elementos na sua aplicação…

Então o ngFor é a arma certa para ter no seu Arsenal de Programador.

Por isso continue lendo esse artigo para descobrir como tirar o melhor proveito desse recurso incrível e aprender mais sobre:

Exibir múltiplos elementos com Angular ngFor

Programador criando múltiplos elementos com angular ngfor

Ngfor é uma diretiva Angular que permite percorrer um array, ou qualquer objeto iterável, e exibir cada item do array como elemento na tela. 

E o que isso deveria significar? 

Colocar o ngFor em um elemento HTML, ou componente, cria várias cópias desse elemento para cada item do array fornecido. 

Alias o Angular ngFor tem o comportamento parecido com o loop for nas linguagens de programação…

Lembre que apesar de serem cópias, os elementos gerados podem variar com base nos valores do array.

Como usar o Angular ngFor?

A escrita do ngFor é simples e bonita, basta colocar no elemento, ou componente, e informar qual array ele deve usar. 

Então primeiro vamos precisar de um array:

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html'
})

export class UserComponent {

  const users = [
    {id: 1, name: 'Gabriel'},
    {id: 2, name: 'Pedro'},
    {id: 3, name: 'Julia'}
  ];

}

Depois fornecemos o array users para a diretiva ngFor dentro de uma tag… no caso a tag li.

Assim criamos um elemento de lista para cada valor de users.

<ul>
  <li *ngFor="let user of users"> {{ user.name }} </li>
</ul>

Primeiro observe que: 

  • A sintaxe of users significa que iremos iterar, ou percorrer, o array de usuários;
  • E depois let user cria uma variável local que disponibiliza o valor de cada usuário. 

Assim você pode personalizar cada elemento com a variável user

Note também que você pode utilizar a variável user em qualquer lugar dentro do ngFor, mas nunca fora. (é parecido com um escopo de função)

Por fim veja o resultado gerado em HTML para o exemplo acima:

<ul>
  <li> Gabriel </li>
  <li> Pedro </li>
  <li> Julia </li>
</ul>

5 variáveis para potencializar seus loops

5 power rangers

O Angular ngFor também fornece vantagens para facilitar o seu trabalho. 

Oferecendo para você algumas variáveis a cada ciclo do ngFor

Então coloque o seu chapéu do Indiana Jones e vamos explorar essas misteriosas variáveis!

Como obter o índice de cada item

Caso você queira numerar os itens da lista, apenas o valor do usuário não é suficiente. 

Então você também precisa obter o índice de cada item. 

Para isso podemos definir uma variável dentro do ngFor e atribuir a ela o valor de index:

<ul>
  <li *ngFor="let user of users; let i = index"> 
    {{ i }}. {{ user.name }} 
  </li>
</ul>

Perceba que chamamos a variável index de “i”, mas você pode nomear como quiser.

Depois basta usar “i” dentro do ngFor, como qualquer outra variável.

Agora veja como fica nossa saída HTML:

<ul>
  <li> 0. Gabriel </li>
  <li> 1. Pedro </li>
  <li> 2. Julia </li>
</ul>

Além disso se você necessita exibir o tamanho total do array, o Angular dispõe outra variável chamada count. (equivalente a users.length)

Com o uso similar ao index:

<ul>
  <li *ngFor="let user of users; let i = index; let c = count"> 
    {{ i }}-{{ c }}. {{ user.name }} 
  </li>
</ul>

Por fim o resultado do HTML gerado fica: 

<ul>
  <li> 0-3. Gabriel </li>
  <li> 1-3. Pedro </li>
  <li> 2-3. Julia </li>
</ul>

Para o nosso array de users, o valor de count é 3 por possuir 3 itens. 

Cuidado para não confundir com o índice do último item, que corresponde ao tamanho total, menos um. (para atender a índices baseados em zero)

Como identificar o primeiro e o último item de um array

Acontece que o índice não é o único valor que podemos obter da diretiva ngFor.

Também existem 2 outras variáveis usadas para identificar o primeiro e o último item do array.

O valor da variável é boolean, dependendo do item atual:

  • first: retorna true se for o primeiro item do array, que corresponde ao índice 0;
  • last: retorna true se for o último item do array.

São muito úteis se você quer estilizar o primeiro ou o último item do array. Usando essas variáveis, podemos atribuir a cada uma delas uma classe CSS diferente:

<ul>
  <li *ngFor="let user of users; let first = first; let last = last"
      [ngClass]="{ inicio-array: first, fim-array: last }">
    {{ user.name }}
  </li>
</ul>

Assim adiciona uma classe CSS:

  • Com nome inicio-array ao primeiro item; 
  • E uma com nome fim-array ao último item.

Usando even e odd para aumentar a legibilidade

Assim como first e last, even e odd são muito úteis para estilizar elementos.

Afinal são usados para identificar itens de índice par (como 2, 4) e itens de índice impar (como 1, 3).

Muito comum na criação de tabelas, afim de aumentar a legibilidade, ao adicionar uma classe CSS diferente às linhas pares ou ímpares.  

O valor da variável também é boolean, dependendo do item atual:

  • even: retorna true para itens pares;
  • odd: retorna true para itens ímpares.

Então se você deseja adicionar uma classe CSS as linhas pares, use even da seguinte maneira: 

<ul>
  <li *ngFor="let user of users; let even = even"
      [ngClass]="{ linha-par: even }">
    {{ user.name }}
  </li>
</ul>

Como melhorar a performance dos seus loops

Foguete representando como melhorar o desempenho do angular ngfor

Agora que você sabe como usar o ngFor, vamos desvendar uma parte importante do seu funcionamento. 

Quando o Angular cria e destruí elementos na página, é uma operação cara em termos de desempenho. (comparando com código Javascript puro

Normalmente a manipulação de elementos na tela ocorre quando: 

  • Um item é adicionado ao array;
  • Um item é removido do array;
  • Ou os itens do array são reordenados.

Mas não dá para você brincar de renderizar elementos…  

Por isso o Angular otimiza as operações e busca reutilizar os elementos DOM existentes. Assim apenas alguns valores serão substituídos. 

Por exemplo, se um item for adicionado ao array, não será renderizada a lista inteira novamente. 

Em vez disso todos os elementos DOM existentes são reutilizados e apenas o elemento adicional é criado.

O mesmo acontece quando um item é removido do array ou o array é reordenando…

Mas como o Angular sabe quais elementos já existem?

Para fazer toda essa otimização, o Angular identifica cada objeto no array através de uma referência. Caso contrário, não poderá determinar “quem é quem”.

Porém o método padrão de identificação de objetos por referência é bastante limitado…

Porque se você cria um novo array com os mesmos valores do antigo e passa esse novo array para o ngFor

  • As referências ainda serão dos objetos do array antigo; 
  • E não será possível identificar que os valores são semelhantes.

Então, para a identificação, o novo array contém um novo conjunto de itens, completamente diferente do anterior. 

Assim o Angular desiste da otimização e renderiza a lista inteira novamente.

Essa situação é muito comum ao solicitar dados do servidor…

E como resolver esse problema?

Aumentando o desempenho do Angular ngFor com a função trackBy

A estratégia padrão de identificação do objeto não é tão ruim…

Como o Angular não possui informações sobre o objeto, então não consegue determinar qual propriedade o torna único.

Para isso você pode alterar o método padrão de identificação do Angular através da função trackBy

Afinal a função informa ao Angular quais propriedades do objeto são exclusivas dele.

No caso do nosso array users, a propriedade exclusiva é o id. Sendo que não existem 2 usuários com o mesmo id

Então devemos retornar o valor do id na função trackBy:

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html'
})

export class UserComponent {
 
  const users = [
    {id: 1, name: 'Gabriel'},
    {id: 2, name: 'Pedro'},
    {id: 3, name: 'Julia'}
  ];

  trackUser(index, user) {
    return user ? user.id : null;
  }

}

Note que a função recebe o índice do item e o próprio item como parâmetros.

Também precisamos informar ao ngFor qual nova função deve usar para rastreamento:

<ul>
  <li *ngFor="let user of users; trackBy : trackUser"> 
    {{ user.name }}
  </li>
</ul>

Mas quando usar o trackBy?

Em principio não é uma alteração padrão…

Como uma otimização de desempenho, é necessário apenas se você tiver problemas de desempenho.

Por exemplo, você está trabalhando com um array muito grande e a aplicação renderiza a lista do zero a cada chamada ao servidor. 

Portanto impacta na velocidade de carregamento e a interface do usuário fica lenta devido à grande quantidade de elementos DOM criados e destruídos.

Parabéns por ter chegado até aqui!

Afinal esse não foi um artigo fácil…

Muitos assuntos foram abordados como:

  • O que é ngFor e como utilizar;
  • Obter o índice de cada item com index;
  • Identificar o primeiro e o último item com first e last;
  • Usar even e odd para aumentar a legibilidade;
  • E aumentar a performance com a função trackBy.

Se você gostou, ou tem alguma dúvida, deixe seu comentário aqui abaixo:

Hey,

o que você achou deste conteúdo? Conte nos comentários.

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

21 Replies to “ngFor Angular – Como criar Poderosos Loops de Repetição”

Mateus Joaquim

Tenho um array e nele percorro um ngFor. Quero fazer ser possivel eu passar ele pra ele mesmo e o ngfor remontar. E possivel ? Exemplo: this.array = […this.array]

Gabriel Nascimento

Não, não é possível. ;(

É necessário você acionar a detecção de alterações manualmente utilizando o ChangeDetectorRef.

Dependendo do caso, a opção trackBy do ngFor pode ajudar.

Irei deixar o link de uma questão no Stack Overflow que trata justamente disso: link aqui

Mauro Souza

Valew Gabriel, o conteúdo ajuda muito. Grande abraço para você.

Gabriel Nascimento

Valeu, Mauro! Grande Abraço!

Ygo Hamon

Muito bom, agora tenho uma duvida, se eu tiver recebendo um array vazio, ou o elemento só venha da api ou service se obtiver algum objeto dentro do array que não seja nulo, o ngFor vai dar erro pois não vai receber nada, como fazer para resolver isso, pois mesmo que tenha um ngIf ele ira dar erro pois o ngFor não ta recebendo nada, assim dando o erro [Object object] no console do navegador.

Gabriel Nascimento

O ngFor precisa receber qualquer objeto iterável

Nesse caso, o erro [Object object] normalmente acontece quando você tenta passar um objeto não iterável para o ngFor.

O que é bastante comum quando você passa um valor vindo de uma API ou service, porque você recebe um objeto de resposta (não iterável).

Por exemplo, 2 situações comuns são:

1 – Passarmos o objeto de resposta inteiro e não apenas a carga útil (que seria o array com os valores, normalmente em um atributo resposta.data);

2 – Ou recebermos um erro como resposta, sem fazer o tratamento adequado. O que faria você atribuir um valor data inexistente ao ngFor.

Pelo seu texto, me parece ser a segunda opção…

Então a solução seria, dentro da lógica que recebe a resposta da API, acrescentar um if para verificar o que está chegando. (E não utilizar o ngIf para tal tratamento)

OBS: É um pouco difícil ter certeza da situação sem ver o código fonte… :/

Mariane

Muito bom, seus conteúdos são incríveis. Apesar de eu ser iniciante tira minhas dúvidas com clareza, muito obrigada.

Gabriel Nascimento

Tamo junto, Mariane! o/ É muito bom ouvir isso. xD

Lucas

excelente!

Gabriel Nascimento

Massa que curtiu! xD

Fabiano

Muito legal tirou multas duvidas que eu tinha com relação ao ngfor. muito bom!

Gabriel Nascimento

Top demais! Fico feliz que você saiu sem dúvidas.

Igor

Muito bom conteúdo, me ajudou, parabéns!!

Gabriel Nascimento

Valeu, Igor!!

julio

Segundo artigo lido hoje e parabéns novamente, conteúdo de angular em português com qualidade são poucos.

Gabriel Nascimento

Caramba, valeu jovem! Fico contente que você achou isso! E parabéns também pela determinação na leitura. 😉

Wilson

Muito bom conteudo e parabens, espero ler mais artigos obrigado

Gabriel Nascimento

Obrigado! Fico muito feliz que tenha gostado. xD

Gabriel Nascimento
João Scheleder

O que eu preciso fazer é colocar uma condicional…

Algo parecido com:

<div *ngFor= "let user of users ; *ngIf: user.type=== 'local' "

Tem como?

Gabriel Nascimento

Não, o Angular não permite o uso do ngFor e ngIf no mesmo elemento.

(Atenção: Não sei se isso irá mudar no futuro, mas no momento funciona assim.)

Você iria precisar fazer algo como:

<div *ngIf="user.type==='local'">
<div *ngFor="let user of users">

</div>
</div>