Built-in structural directives
Ao iniciar os estudos em Angular, aprendemos como criar Diretivas Estruturais utilizando por exemplo *ngIf e *ngSwitch para condicionais e *ngFor para iterar dinamicamente por itens de uma lista, mas para isso precisamos do CommonModule (no standalone component ou em lazy loaded modules). Vamos utilizar o exemplo a seguir a partir de uma lista de tarefas a serem feitas (Todo Items):
src/app/app.component.ts
import { Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { HttpClient } from '@angular/common/http';
import { CommonModule } from '@angular/common';
interface Todo {
userId: number;
id: number;
title: string;
completed: boolean;
}
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
private httpClient = inject(HttpClient);
todos$ = this.httpClient.get<Todo[]>('https://jsonplaceholder.typicode.com/todos');
todos = toSignal(this.todos$);
}
src/app/app.component.html
<ul>
<ng-container *ngIf="todos(); else loading">
<ng-container *ngIf="todos()!.length; else empty">
<li *ngFor="let todo of todos()">
<input type="checkbox" [checked]="todo.completed" />
{{ todo.title }}
</li>
</ng-container>
</ng-container>
<ng-template #loading>
<p>Carregando...</p>
</ng-template>
<ng-template #empty>
<p>Não há todos</p>
</ng-template>
</ul>
Podemos ver que não é possível utilizar *ngIf e *ngFor no mesmo elemento html <li>, e para não adicionarmos uma div adicional ao DOM, acabamos precisando usar <ng-container> e também <ng-template> para o fluxo de else, o que nos obriga a escrever muitas instruções adicionais no template!
New Control flow Syntax
Neste artigo vamos falar um pouco sobre algumas melhorias introduzidas a partir da v17 como Developer Preview e na v18 como stable para produção, e mostrar que embora havia uma outra forma de ser feito, as novidades ajudaram a melhorar demais a experiência nossa como desenvolvedor (DX), vamos conferir a seguir a nova sintaxe de Control Flow!
@if e @else
Com a nova sintaxe em bloco fica bem mais legível, e podemos utilizar também além do @if, @else if e @else, desta forma não precisamos mais do <ng-container> e do <ng-template>.
@for e @empty
No caso do @for agora é obrigatório o uso da expressão track (o que aumenta consideravelmente performance com um esforço reduzido), além de poder usar o @empty como sendo um "else" do for, no caso do array ser vazio, e você gostaria de renderizar uma mensagem como empty state.
Refatorando
Vamos refatorar o código abaixo trocando o *ngIf por @if e *ngFor por @for e conferir o resultado final:
src/app/app.component.ts
import { Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { HttpClient } from '@angular/common/http';
interface Todo {
userId: number;
id: number;
title: string;
completed: boolean;
}
@Component({
selector: 'app-root',
standalone: true,
imports: [],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
private httpClient = inject(HttpClient);
todos$ = this.httpClient.get<Todo[]>('https://jsonplaceholder.typicode.com/todos');
todos = toSignal(this.todos$);
}
src/app/app.component.html
<ul>
@if (todos()) {
@for (todo of todos(); track todo.id) {
<li>
<input type="checkbox" [checked]="todo.completed" />
{{ todo.title }}
</li>
} @empty {
<p>Não há todos</p>
}
} @else {
<p>Carregando...</p>
}
</ul>
Migração
Mesmo que você utilize módulos, a partir da versão v17 já pode utilizar esta nova sintaxe que além de importar arquivos javascript a menos, ainda é bem mais legível! Caso seu projeto seja grande, você pode utilizar para auxiliar o angular também já fornece o schematic ng generate @angular/core:control-flow
, o link da documentação está aqui.
Compartilhe com seus amigos que gostam de se informar sobre as novidades atuais do framework e até o próximo artigo! Grande abraço!