Angular do Zero a 100 Exemplos: Componentes, Templates, Signals, Formulários, Rotas, HTTP e Todos os Tipos de Propriedade
Angular é um framework do Google para criar aplicações web grandes e bem estruturadas com TypeScript. Este artigo vai da criação do projeto até 100 exemplos executáveis. Você vai estudar componentes e templates, a sintaxe de controle de fluxo, pipes, inputs e outputs, todos os tipos de propriedade que um componente pode ter, lifecycle hooks, injeção de dependência, rotas, formulários reativos e baseados em template, HTTP com RxJS, diretivas, signals, testes e deploy. Cada exemplo é um trecho curto. Os trechos de TypeScript foram verificados com o parser do TypeScript 5.6; os templates e os comandos de build são mostrados como são escritos em um projeto Angular 19.
O que você precisa
Node.js 20 ou mais recente com npm, um editor de código como o Visual Studio Code e um navegador. Instale o Angular CLI com npm install -g @angular/cli. É bom conhecer HTML, CSS e JavaScript básicos. Saber o que são classes e funções ajuda a entender os exemplos de TypeScript.
Como usar um exemplo
Os trechos de TypeScript entram em um projeto Angular independente, no arquivo do componente ou serviço mencionado. Os comandos são executados no terminal do projeto, e os exemplos de HTML são templates para o arquivo HTML do componente.
Tópico 1: Configuração do projeto e primeiro componente
Uma aplicação Angular é uma árvore de componentes. O CLI cria o projeto, e cada componente é uma classe com um decorator que define seu seletor e seu template.
Criando o projeto
ng new cria um projeto com arquivos para uma aplicação standalone, o que significa que os componentes não precisam de NgModule. ng serve roda o servidor de desenvolvimento com recarga automática, e ng generate cria arquivos para um novo componente, serviço ou pipe.
Bootstrap e configuração
main.ts chama bootstrapApplication com o componente raiz e a configuração da aplicação. Essa configuração registra os providers, como o roteador e o cliente HTTP, em um único lugar.
Componente, seletor e estilos
O decorator @Component define o seletor, que é a tag HTML, o template e os estilos. Os estilos valem só para aquele componente, porque o Angular adiciona um atributo único aos seus elementos, então dois componentes podem ter uma classe chamada card sem conflito.
Ilustração
Exemplo 1: Criando o projeto com o CLI
Instale o Angular CLI e crie uma aplicação standalone.
npm install -g @angular/cli
ng new blog --standalone --routing --style=css
cd blog
ng serve
Exemplo 2: Criando um componente
O CLI cria os arquivos de um novo componente.
ng generate component hello
# forma curta
ng g c hello --standalone
Exemplo 3: Bootstrap da aplicação
Inicie o componente raiz com a configuração da aplicação.
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));
Exemplo 4: Componente standalone
Um componente que não precisa de NgModule.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
standalone: true,
template: '<h1>Hello, Angular!</h1>',
})
export class AppComponent {}
Exemplo 5: Template inline com várias linhas
Escreva o template em várias linhas usando crases.
import { Component } from '@angular/core';
@Component({
selector: 'app-card',
standalone: true,
template: `
<section>
<h2>Welcome</h2>
<p>This template spans several lines.</p>
</section>
`,
})
export class CardComponent {}
Exemplo 6: Estilo do componente
Um estilo que vale apenas para este componente.
import { Component } from '@angular/core';
@Component({
selector: 'app-badge',
standalone: true,
template: '<span class="badge">New</span>',
styles: [`.badge { background: #3366ff; color: white; padding: 4px 8px; border-radius: 6px; }`],
})
export class BadgeComponent {}
Exemplo 7: Configuração da aplicação com providers
Registre o roteador e outros serviços em um único lugar.
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes)],
};
Exemplo 8: Elemento raiz no index.html
app-root é o lugar onde o Angular desenha a aplicação.
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<title>Blog</title>
<base href="/">
</head>
<body>
<app-root></app-root>
</body>
</html>
Tópico 2: Templates e data binding
Um template é HTML com uma sintaxe extra. Ele exibe valores do componente, envia as ações do usuário de volta e mantém entradas e valores sincronizados.
Interpolação e expressões
Chaves duplas exibem um valor dentro do texto. As expressões podem fazer contas, usar o operador ternário ou o operador de navegação segura ?., para que um objeto inexistente não cause erro.
Binding de propriedade, de atributo e de classe
Colchetes vinculam uma propriedade do DOM, como [disabled]. O binding de atributo com attr. é necessário para atributos HTML que não têm propriedade correspondente, como o rótulo aria. O binding de classe e de estilo adiciona uma classe ou um estilo inline a partir de um valor.
Binding de evento e two-way binding
Parênteses escutam um evento, como (click). O two-way binding com [(ngModel)] mantém o campo do formulário e o valor do componente iguais, e exige FormsModule. Uma variável de referência de template, como #box, dá um nome ao elemento para o restante do template.
Exemplo 9: Interpolação
Exiba valores dentro do texto com chaves duplas.
import { Component } from '@angular/core';
@Component({
selector: 'app-greeting',
standalone: true,
template: '<p>Hello, {{ name }}! You have {{ count + 1 }} message(s).</p>',
})
export class GreetingComponent {
name = 'Ada';
count = 2;
}
Exemplo 10: Expressões no template
Calcule valores, use ternário e navegação segura no template.
<p>{{ price * quantity }}</p>
<p>{{ isLoggedIn ? 'Welcome back' : 'Please sign in' }}</p>
<p>{{ user?.name ?? 'Guest' }}</p>
Exemplo 11: Property binding
Defina propriedades de um elemento a partir de valores do componente.
<img [src]="photoUrl" [alt]="photoAlt" width="120">
<button [disabled]="saving">Save</button>
Exemplo 12: Attribute binding
Defina atributos HTML que não têm propriedade correspondente.
<button [attr.aria-label]="closeLabel">X</button>
<td [attr.colspan]="columns">Total</td>
Exemplo 13: Event binding
Execute um método do componente quando o usuário clicar.
import { Component } from '@angular/core';
@Component({
selector: 'app-counter',
standalone: true,
template: `
<p>Clicked {{ clicks }} times</p>
<button (click)="increment()">Click me</button>
`,
})
export class CounterComponent {
clicks = 0;
increment() {
this.clicks++;
}
}
Exemplo 14: Two-way binding com ngModel
Mantenha o input e o valor do componente sincronizados.
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-name-field',
standalone: true,
imports: [FormsModule],
template: `
<input [(ngModel)]="name">
<p>You typed: {{ name }}</p>
`,
})
export class NameFieldComponent {
name = '';
}
Exemplo 15: Binding de classe
Adicione ou remova classes CSS a partir de um valor.
<div [class.active]="isActive" [class.error]="hasError">Panel</div>
<div [ngClass]="{ 'big': size === 'lg', 'hidden': !visible }">Box</div>
Exemplo 16: Binding de estilo
Defina estilos inline a partir de valores do componente.
<p [style.color]="textColor" [style.font-size.px]="fontSize">Styled text</p>
Exemplo 17: Variável de referência de template
Dê um nome ao elemento para que outras partes do template o usem.
<input #nameBox type="text">
<button (click)="greet(nameBox.value)">Greet</button>
Tópico 3: Controle de fluxo: @if, @for, @switch e @defer
Desde o Angular 17, os templates têm blocos integrados para condições e repetições. Esses blocos substituem as diretivas estruturais antigas e são mais fáceis de ler.
@if e @else
@if mostra o bloco somente quando a condição é verdadeira. @else if e @else escolhem entre vários blocos. Os blocos podem ser aninhados, então um if pode estar dentro de um for.
@for com track e @empty
@for repete um bloco para cada item. track diz ao Angular qual item é qual, para que só as linhas que mudaram sejam redesenhadas. @empty mostra uma mensagem quando a lista está vazia. track é obrigatório.
@switch e @defer
@switch escolhe um bloco pelo valor de uma expressão, com @default como alternativa. @defer carrega uma parte da página depois, por exemplo quando ela aparece na tela ou quando o usuário interage com ela, e mostra @placeholder ou @loading até lá.
Exemplo 18: @if
Mostre o bloco somente quando a condição for verdadeira.
@if (items.length > 0) {
<p>You have {{ items.length }} items.</p>
}
Exemplo 19: @if com @else
Escolha entre vários blocos.
@if (isLoggedIn) {
<p>Welcome back!</p>
} @else if (isGuest) {
<p>Browsing as a guest.</p>
} @else {
<p>Please sign in.</p>
}
Exemplo 20: @for com track
Repita o bloco para cada item; track dá a cada item uma identidade estável.
<ul>
@for (fruit of fruits; track fruit.id) {
<li>{{ fruit.name }}</li>
}
</ul>
Exemplo 21: @for com @empty
Mostre uma mensagem quando a lista não tem itens.
@for (post of posts; track post.id) {
<article>{{ post.title }}</article>
} @empty {
<p>No posts yet.</p>
}
Exemplo 22: @switch
Escolha um bloco pelo valor de uma expressão.
@switch (status) {
@case ('draft') { <span>Draft</span> }
@case ('published') { <span>Live</span> }
@default { <span>Unknown</span> }
}
Exemplo 23: @defer com placeholder
Carregue uma parte da página depois, mostrando um placeholder antes.
@defer (on viewport) {
<app-heavy-chart />
} @placeholder {
<p>Chart will load when visible.</p>
} @loading {
<p>Loading chart...</p>
}
Exemplo 24: @let para valores locais
Dê nome a um valor uma vez e use-o várias vezes no template.
@let total = price * quantity;
<p>Total: {{ total }}</p>
Tópico 4: Pipes
Um pipe transforma um valor dentro do template, por exemplo para formatar uma data ou um valor monetário. O valor no componente continua igual.
Pipes de formatação integrados
date, currency, number, uppercase, lowercase, titlecase e slice formatam valores dentro do template. Passe argumentos depois dos dois-pontos, como date: 'longDate' ou currency: 'USD'.
json, keyvalue e async
json mostra um objeto como texto, o que é útil ao depurar. keyvalue percorre as chaves e os valores de um objeto. async assina um Observable e mostra o valor mais recente, e cancela a assinatura automaticamente quando o componente é destruído.
Pipe personalizado
Uma classe com o decorator @Pipe que implementa transform define um pipe novo. Crie um pipe quando você formatar o mesmo tipo de valor em vários lugares, e mantenha-o sem efeitos colaterais para que o Angular possa reutilizar o resultado.
Exemplo 25: Pipe date
Formate datas para exibição.
<p>{{ today | date: 'longDate' }}</p>
<p>{{ today | date: 'shortTime' }}</p>
Exemplo 26: Pipes currency e number
Formate valores monetários e números decimais.
<p>{{ 1234.5 | currency: 'USD' }}</p>
<p>{{ 3.14159 | number: '1.2-2' }}</p>
Exemplo 27: Pipes de texto
Mude maiúsculas e minúsculas ou corte parte de uma string no template.
<p>{{ 'angular' | uppercase }}</p>
<p>{{ 'HELLO' | lowercase }}</p>
<p>{{ 'hello world' | titlecase }}</p>
<p>{{ 'Angular' | slice: 0 : 3 }}</p>
Exemplo 28: Pipes json e keyvalue
Mostre um objeto para depuração, ou percorra suas chaves e valores.
<pre>{{ user | json }}</pre>
<ul>
@for (item of settings | keyvalue; track item.key) {
<li>{{ item.key }}: {{ item.value }}</li>
}
</ul>
Exemplo 29: Pipe async
Assine um Observable e mostre o valor mais recente.
import { Component } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { Observable, of } from 'rxjs';
@Component({
selector: 'app-clock',
standalone: true,
imports: [AsyncPipe],
template: '<p>Value: {{ value$ | async }}</p>',
})
export class ClockComponent {
value$: Observable<number> = of(42);
}
Exemplo 30: Pipe personalizado
Escreva seu próprio pipe que encurta um texto.
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'shorten', standalone: true })
export class ShortenPipe implements PipeTransform {
transform(value: string, max = 20): string {
return value.length > max ? value.slice(0, max) + '...' : value;
}
}
Tópico 5: Componentes que se comunicam
Os dados fluem de cima para baixo, do pai para o filho, por meio de inputs, e os eventos fluem de baixo para cima, por meio de outputs. Componentes que não são pai e filho podem compartilhar um serviço.
Inputs
input() declara um valor definido pelo pai. Dê um valor padrão, use input.required quando o pai precisar definir esse valor, ou adicione um alias quando o nome do atributo precisar ser diferente. O decorator @Input mais antigo continua funcionando.
Outputs e @Output
output() declara um evento que o filho emite com .emit(). O pai escuta com um event binding, como (liked)='save()'. @Output com EventEmitter, mais antigo, funciona da mesma forma.
Projeção, consultas e serviços compartilhados
ng-content permite que o pai coloque seu próprio conteúdo dentro do filho. viewChild e contentChild encontram um elemento ou template pelo nome. Um serviço compartilhado com um Subject ou um signal permite que componentes distantes se comuniquem.
Exemplo 31: Signal input()
Receba um valor do pai com um valor padrão.
import { Component, input } from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
template: '<p>Hello, {{ name() }}</p>',
})
export class UserCardComponent {
name = input<string>('Guest');
}
Exemplo 32: Input obrigatório
Obriga o pai a fornecer o valor.
import { Component, input } from '@angular/core';
@Component({
selector: 'app-price',
standalone: true,
template: '<p>{{ amount() }} THB</p>',
})
export class PriceComponent {
amount = input.required<number>();
}
Exemplo 33: Output com output()
Envie um evento do filho para o pai.
import { Component, output } from '@angular/core';
@Component({
selector: 'app-like-button',
standalone: true,
template: '<button (click)="liked.emit()">Like</button>',
})
export class LikeButtonComponent {
liked = output<void>();
}
Exemplo 34: EventEmitter com @Output
O jeito mais antigo de emitir um evento a partir de um componente filho.
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-search-box',
standalone: true,
template: '<input (keyup.enter)="submit($event)">',
})
export class SearchBoxComponent {
@Output() searched = new EventEmitter<string>();
submit(event: Event) {
this.searched.emit((event.target as HTMLInputElement).value);
}
}
Exemplo 35: Consulta de signal com viewChild
Obtenha uma referência a um elemento dentro do template do componente.
import { Component, ElementRef, viewChild } from '@angular/core';
@Component({
selector: 'app-focus',
standalone: true,
template: '<input #box type="text">',
})
export class FocusComponent {
box = viewChild<ElementRef<HTMLInputElement>>('box');
focusBox() {
this.box()?.nativeElement.focus();
}
}
Exemplo 36: Projeção de conteúdo com ng-content
Deixe o pai colocar seu próprio conteúdo dentro do filho.
import { Component } from '@angular/core';
@Component({
selector: 'app-panel',
standalone: true,
template: `
<section class="panel">
<ng-content></ng-content>
</section>
`,
})
export class PanelComponent {}
Exemplo 37: contentChild e ng-template
Receba um template do pai e renderize-o para cada item.
import { Component, contentChild, TemplateRef } from '@angular/core';
@Component({
selector: 'app-list',
standalone: true,
imports: [],
template: `
@for (item of items; track item) {
<ng-container [ngTemplateOutlet]="itemTpl()" [ngTemplateOutletContext]="{ $implicit: item }"></ng-container>
}
`,
})
export class ListComponent {
items = ['a', 'b'];
itemTpl = contentChild<TemplateRef<unknown>>('itemTpl');
}
Exemplo 38: Host binding e host listener
Vincule-se ao elemento host e escute os eventos dele.
import { Component, HostBinding, HostListener } from '@angular/core';
@Component({
selector: 'app-hover-card',
standalone: true,
template: '<p>Hover me</p>',
})
export class HoverCardComponent {
@HostBinding('class.highlighted') highlighted = false;
@HostListener('mouseenter') onEnter() {
this.highlighted = true;
}
@HostListener('mouseleave') onLeave() {
this.highlighted = false;
}
}
Exemplo 39: Metadados do host
Defina classes e atributos do host dentro do decorator.
import { Component } from '@angular/core';
@Component({
selector: 'app-tag',
standalone: true,
template: '<ng-content></ng-content>',
host: {
'role': 'note',
'[class.small]': 'small',
},
})
export class TagComponent {
small = true;
}
Exemplo 40: Serviço compartilhado para mensagens
Envie mensagens entre componentes que não são pai e filho.
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class MessageBus {
private messages = new Subject<string>();
messages$ = this.messages.asObservable();
send(text: string) {
this.messages.next(text);
}
}
Tópico 6: Propriedades de todos os tipos
Um componente pode ter vários tipos de propriedade. Cada tipo serve a uma necessidade: valor comum, valor somente leitura, valor derivado, signal reativo e valor de duas vias.
Propriedades comuns, readonly e opcionais
Uma propriedade comum é um campo com valor, que pode mudar. readonly impede uma nova atribuição depois que o campo recebe seu valor. O ponto de interrogação em uma propriedade, como email?, indica que o valor pode não existir, então o código precisa lidar com undefined.
Getters, setters e propriedades estáticas
Um getter executa código quando a propriedade é lida, e um setter executa código quando ela é escrita. Uma propriedade estática pertence à própria classe, e não a cada instância, e é adequada para valores padrão compartilhados, como o nome de um tema.
Signals: signal, computed e effect
signal() guarda um valor e avisa o Angular quando ele muda; você lê o signal como uma função, como count(). computed() deriva um valor a partir de signals e é atualizado quando eles mudam. effect() executa código sempre que um signal que ele lê muda.
model(), alias de input e input.required
model() é um input de duas vias: o filho pode definir o valor, e o valor do pai acompanha. É semelhante ao ngModel para componentes personalizados. Um alias dá ao input um nome de atributo diferente. input.required obriga o pai a fornecer o valor.
Propriedades com funções arrow
Uma propriedade que guarda uma função arrow mantém o this do componente quando a função é passada como callback, porque a arrow function captura o this no momento em que é criada.
Exemplo 41: Propriedade de classe com valor padrão
Uma propriedade comum com valor inicial.
import { Component } from '@angular/core';
@Component({
selector: 'app-profile',
standalone: true,
template: '<p>{{ username }} ({{ role }})</p>',
})
export class ProfileComponent {
username = 'mali';
role: string = 'editor';
}
Exemplo 42: Propriedade readonly
Uma propriedade que só pode receber valor quando é declarada.
import { Component } from '@angular/core';
@Component({
selector: 'app-version',
standalone: true,
template: '<p>Version {{ version }}</p>',
})
export class VersionComponent {
readonly version = '1.4.0';
}
Exemplo 43: Propriedade com getter e setter
Uma propriedade que executa código ao ser lida ou escrita.
import { Component } from '@angular/core';
@Component({
selector: 'app-temperature',
standalone: true,
template: '<p>{{ fahrenheit }} F</p>',
})
export class TemperatureComponent {
private celsius = 0;
get fahrenheit(): number {
return this.celsius * 9 / 5 + 32;
}
set fahrenheit(value: number) {
this.celsius = (value - 32) * 5 / 9;
}
}
Exemplo 44: Propriedade signal com set e update
Um valor reativo que você altera com set ou update.
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-counter-signal',
standalone: true,
template: `
<p>{{ count() }}</p>
<button (click)="count.update(n => n + 1)">+1</button>
<button (click)="count.set(0)">Reset</button>
`,
})
export class CounterSignalComponent {
count = signal(0);
}
Exemplo 45: Propriedade computed
Um valor derivado de signals que se atualiza sozinho.
import { Component, computed, signal } from '@angular/core';
@Component({
selector: 'app-cart',
standalone: true,
template: '<p>Total: {{ total() }}</p>',
})
export class CartComponent {
prices = signal([120, 80, 50]);
total = computed(() => this.prices().reduce((sum, p) => sum + p, 0));
}
Exemplo 46: effect
Execute código sempre que o signal que ele lê mudar.
import { Component, effect, signal } from '@angular/core';
@Component({
selector: 'app-theme',
standalone: true,
template: '<p>Theme: {{ theme() }}</p>',
})
export class ThemeComponent {
theme = signal('light');
constructor() {
effect(() => {
console.log('Theme changed to', this.theme());
});
}
}
Exemplo 47: Input de duas vias com model()
Um input que o filho também pode atualizar, como o ngModel.
import { Component, model } from '@angular/core';
@Component({
selector: 'app-toggle',
standalone: true,
template: '<button (click)="on.set(!on())">{{ on() ? "On" : "Off" }}</button>',
})
export class ToggleComponent {
on = model(false);
}
Exemplo 48: Input com alias
Use um nome de atributo diferente no template.
import { Component, input } from '@angular/core';
@Component({
selector: 'app-avatar',
standalone: true,
template: '<img [alt]="label()">',
})
export class AvatarComponent {
label = input('Profile picture', { alias: 'caption' });
}
Exemplo 49: Propriedade opcional
Uma propriedade que pode não existir, marcada com um ponto de interrogação.
interface Customer {
id: number;
name: string;
email?: string;
}
const shopper: Customer = { id: 1, name: 'Ploy' };
console.log(shopper.email ?? 'no email');
Exemplo 50: Propriedade estática e função arrow
Um valor para a classe inteira, e uma função que mantém o seu this.
import { Component } from '@angular/core';
@Component({
selector: 'app-config',
standalone: true,
template: '<p>{{ appName }}</p>',
})
export class ConfigComponent {
static readonly defaultTheme = 'light';
appName = 'Blog';
logName = () => console.log(this.appName);
}
Tópico 7: Lifecycle hooks
O Angular chama métodos especiais em pontos fixos da vida de um componente: quando ele é criado, quando seus inputs mudam, depois que sua view é desenhada e antes de ser removido.
ngOnInit e ngOnChanges
ngOnInit roda uma vez, depois que o Angular define os primeiros inputs, por isso é o lugar certo para carregar dados. ngOnChanges roda sempre que um input muda, e recebe os valores antigo e novo em SimpleChanges.
ngOnDestroy
ngOnDestroy roda logo antes de o componente ser removido. Cancele a assinatura de streams de longa duração e limpe timers ali para evitar vazamentos de memória.
ngAfterViewInit e ngDoCheck
ngAfterViewInit roda depois que a view do componente e as views dos filhos são desenhadas, então os elementos da view já estão disponíveis. ngDoCheck roda em cada ciclo de detecção de mudanças, então mantenha-o leve.
Exemplo 51: ngOnInit
Execute o código de preparação uma vez, depois que o Angular define os inputs.
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-todo-list',
standalone: true,
template: '<p>{{ items.length }} tasks loaded</p>',
})
export class TodoListComponent implements OnInit {
items: string[] = [];
ngOnInit() {
this.items = ['Write', 'Test', 'Ship'];
}
}
Exemplo 52: ngOnChanges
Reaja quando um input vindo do pai muda.
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
@Component({
selector: 'app-badge-count',
standalone: true,
template: '<span>{{ count }}</span>',
})
export class BadgeCountComponent implements OnChanges {
@Input() count = 0;
ngOnChanges(changes: SimpleChanges) {
if (changes['count']) {
console.log('Count changed to', changes['count'].currentValue);
}
}
}
Exemplo 53: ngOnDestroy
Limpe assinaturas e timers quando o componente for removido.
import { Component, OnDestroy, OnInit } from '@angular/core';
import { interval, Subscription } from 'rxjs';
@Component({
selector: 'app-timer',
standalone: true,
template: '<p>Ticks: {{ ticks }}</p>',
})
export class TimerComponent implements OnInit, OnDestroy {
ticks = 0;
private sub?: Subscription;
ngOnInit() {
this.sub = interval(1000).subscribe(() => this.ticks++);
}
ngOnDestroy() {
this.sub?.unsubscribe();
}
}
Exemplo 54: ngAfterViewInit
Use um elemento da view depois que o template foi desenhado.
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-autofocus',
standalone: true,
template: '<input #field>',
})
export class AutofocusComponent implements AfterViewInit {
@ViewChild('field') field!: ElementRef<HTMLInputElement>;
ngAfterViewInit() {
this.field.nativeElement.focus();
}
}
Exemplo 55: ngDoCheck
Execute uma verificação em cada ciclo de detecção de mudanças.
import { Component, DoCheck } from '@angular/core';
@Component({
selector: 'app-watcher',
standalone: true,
template: '<p>Checks run: {{ checks }}</p>',
})
export class WatcherComponent implements DoCheck {
checks = 0;
ngDoCheck() {
this.checks++;
}
}
Tópico 8: Injeção de dependência e serviços
Um serviço guarda a lógica e os dados que vários componentes precisam. O Angular cria o serviço e o entrega por injeção de dependência, assim os componentes não criam cópias próprias.
@Injectable e providedIn root
Uma classe com @Injectable({ providedIn: 'root' }) é criada uma vez para a aplicação inteira. Um componente a obtém com inject() ou com um parâmetro do construtor. Use signals dentro do serviço para guardar um estado que o template pode ler.
InjectionToken e provider de valor
Um token identifica um valor que não é uma classe, como um objeto de configuração. Você o registra com um provider: useValue para um valor fixo, useClass para uma classe, ou useFactory para construir o valor.
Providers no nível do componente
Um array providers em um componente dá a esse componente e aos seus filhos uma instância própria do serviço. Isso é útil para um estado que deve ser reiniciado sempre que o componente é criado.
Exemplo 56: Função inject()
Obtenha um serviço sem um parâmetro no construtor.
import { Component, inject } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-items',
standalone: true,
template: '<p>{{ data.count() }} items</p>',
})
export class ItemsComponent {
data = inject(DataService);
}
Exemplo 57: Serviço injetável
Uma classe compartilhada que qualquer componente pode receber.
import { Injectable, signal } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class CartService {
private itemsSignal = signal<string[]>([]);
readonly items = this.itemsSignal.asReadonly();
add(item: string) {
this.itemsSignal.update((list) => [...list, item]);
}
}
Exemplo 58: InjectionToken
Forneça um valor que não é uma classe, como uma configuração.
import { InjectionToken } from '@angular/core';
export interface AppSettings {
apiUrl: string;
pageSize: number;
}
export const APP_SETTINGS = new InjectionToken<AppSettings>('app.settings');
Exemplo 59: Provider com useValue
Forneça um objeto fixo ao injector.
import { ApplicationConfig } from '@angular/core';
import { APP_SETTINGS } from './app-settings.token';
export const appConfig: ApplicationConfig = {
providers: [
{
provide: APP_SETTINGS,
useValue: { apiUrl: 'https://api.example.com', pageSize: 20 },
},
],
};
Exemplo 60: useClass e useFactory
Escolha uma classe ou construa um valor quando for necessário.
import { ApplicationConfig, inject } from '@angular/core';
import { LoggerService } from './logger.service';
import { ConsoleLogger } from './console-logger';
export const appConfig: ApplicationConfig = {
providers: [
{ provide: LoggerService, useClass: ConsoleLogger },
{
provide: 'clock',
useFactory: () => () => new Date().getFullYear(),
},
],
};
Exemplo 61: Provider no nível do componente
Dê uma instância própria do serviço a uma árvore de componentes.
import { Component, inject } from '@angular/core';
import { CounterStore } from './counter.store';
@Component({
selector: 'app-scoped-counter',
standalone: true,
template: '<p>{{ store.value() }}</p>',
providers: [CounterStore],
})
export class ScopedCounterComponent {
store = inject(CounterStore);
}
Tópico 9: Roteamento
O roteador associa caminhos de URL a componentes, mostra a página correspondente no router-outlet e pode carregar uma página somente quando ela é necessária.
Rotas e links de navegação
O array Routes associa caminhos como '' e 'about' a componentes. routerLink cria links sem recarregar a página, e routerLinkActive adiciona uma classe ao link da página atual. A rota curinga ** trata URLs desconhecidas.
Parâmetros e lazy loading
Uma rota como 'post/:id' passa o id, que você lê em ActivatedRoute. loadComponent com import dinâmico carrega o código da página somente quando o usuário a abre, o que mantém pequeno o download inicial.
Guards, resolvers e navegação por código
Um guard CanActivate devolve true ou uma UrlTree para redirecionar. Um resolver carrega dados antes de a página abrir. Router.navigate move o usuário a partir do código e pode passar query parameters.
Exemplo 62: Routes
Associe caminhos de URL a componentes.
import { Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
export const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: '**', redirectTo: '' },
];
Exemplo 63: routerLink e router-outlet
Conecte as páginas entre si e mostre a página correspondente.
<nav>
<a routerLink="/" routerLinkActive="active">Home</a>
<a routerLink="/about" routerLinkActive="active">About</a>
</nav>
<router-outlet></router-outlet>
Exemplo 64: Parâmetros de rota
Leia valores como um id da URL.
import { Component, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-post',
standalone: true,
template: '<p>Post id: {{ id }}</p>',
})
export class PostComponent {
private route = inject(ActivatedRoute);
id = this.route.snapshot.paramMap.get('id');
}
Exemplo 65: Rota com lazy loading
Carregue o código da página somente quando o usuário a abrir.
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'admin',
loadComponent: () =>
import('./admin/admin.component').then((m) => m.AdminComponent),
},
];
Exemplo 66: Guard CanActivate
Permita ou bloqueie uma rota com base em uma verificação.
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';
export const authGuard: CanActivateFn = () => {
const auth = inject(AuthService);
return auth.isLoggedIn() ? true : inject(Router).createUrlTree(['/login']);
};
Exemplo 67: Resolver
Carregue dados antes de a página abrir.
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { PostService } from './post.service';
export const postResolver: ResolveFn<string> = (route) => {
return inject(PostService).titleFor(route.paramMap.get('id') ?? '');
};
Exemplo 68: Navegação programática
Mude de página pelo código, com query parameters.
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-checkout',
standalone: true,
template: '<button (click)="finish()">Finish</button>',
})
export class CheckoutComponent {
private router = inject(Router);
finish() {
this.router.navigate(['/thanks'], { queryParams: { ref: 'cart' } });
}
}
Tópico 10: Formulários
O Angular tem dois estilos de formulário. Formulários reativos são construídos no código e são a escolha comum para formulários grandes. Formulários baseados em template usam ngModel no HTML e servem para formulários pequenos.
FormControl, FormGroup e FormBuilder
FormControl guarda um único valor, e FormGroup agrupa vários controles. FormBuilder reduz o código necessário, e nonNullable mantém o valor como string, e não como null, depois de um reset.
Validadores e mensagens de erro
Validators.required, minLength, min, max e email já existem. Mostre um erro somente quando o controle é inválido e já foi tocado, para que o usuário não veja erros antes de digitar. Um validador personalizado é uma função que devolve um objeto de erro ou null.
FormArray e valueChanges
FormArray é uma lista de controles que você pode adicionar ou remover. valueChanges é um Observable que emite o valor sempre que ele muda, útil para busca em tempo real ou para salvar um rascunho.
Exemplo 69: FormControl reativo
Um campo de formulário cujo valor fica no código.
import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-email-input',
standalone: true,
imports: [ReactiveFormsModule],
template: '<input [formControl]="email">',
})
export class EmailInputComponent {
email = new FormControl('', { nonNullable: true });
}
Exemplo 70: FormGroup com FormBuilder
Agrupe vários campos em um único formulário reativo.
import { Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-signup',
standalone: true,
imports: [ReactiveFormsModule],
template: '<form [formGroup]="form"></form>',
})
export class SignupComponent {
private fb = inject(FormBuilder);
form = this.fb.group({
name: [''],
email: [''],
});
}
Exemplo 71: Validadores integrados
Verificações de required, minLength, min, max e email.
import { FormBuilder, Validators } from '@angular/forms';
import { inject } from '@angular/core';
const fb = inject(FormBuilder);
const form = fb.nonNullable.group({
name: ['', [Validators.required, Validators.minLength(2)]],
age: [18, [Validators.min(13), Validators.max(120)]],
email: ['', Validators.email],
});
Exemplo 72: Template de formulário reativo
Ligue o FormGroup ao HTML e mostre os erros.
<form [formGroup]="form" (ngSubmit)="save()">
<input formControlName="name" placeholder="Name">
@if (form.controls.name.invalid && form.controls.name.touched) {
<p class="error">Name is required.</p>
}
<button [disabled]="form.invalid">Save</button>
</form>
Exemplo 73: FormArray
Uma lista de controles de formulário que pode crescer ou diminuir.
import { FormArray, FormControl } from '@angular/forms';
const tags = new FormArray([
new FormControl('angular'),
new FormControl('typescript'),
]);
tags.push(new FormControl('rxjs'));
console.log(tags.length);
Exemplo 74: Validador personalizado
Escreva sua própria regra, como não permitir espaços.
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
export function noSpaces(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const value = String(control.value ?? '');
return value.includes(' ') ? { noSpaces: true } : null;
};
}
Exemplo 75: Formulário baseado em template
Monte o formulário com ngModel diretamente no template.
<form #form="ngForm" (ngSubmit)="submit(form.value)">
<input name="title" ngModel required>
<button [disabled]="form.invalid">Post</button>
</form>
Exemplo 76: valueChanges
Reaja sempre que o valor do formulário mudar.
import { FormControl } from '@angular/forms';
const search = new FormControl('');
search.valueChanges.subscribe((text) => {
console.log('Searching for', text);
});
Tópico 11: HTTP, RxJS e dados assíncronos
HttpClient envia requisições e devolve um Observable. Os operadores do RxJS transformam esse stream, e o pipe async mostra o resultado sem assinatura manual.
Configurando o HttpClient
Chame provideHttpClient() na configuração da aplicação. HttpClient.get e post devolvem um Observable, que não faz nada até que alguém se inscreva, então use o pipe async ou toSignal no template.
Operadores: map, filter e switchMap
map transforma cada valor, e filter guarda apenas os valores que você quer. switchMap cancela a requisição anterior quando chega um novo valor, o que é a ferramenta certa para uma busca enquanto o usuário digita. Acrescente debounceTime para esperar o usuário parar de digitar.
Erros e retry
catchError trata uma requisição que falhou e pode devolver um valor seguro. retry tenta a requisição de novo antes de falhar. Decida sempre o que o usuário vê quando há um erro, porque uma tela vazia confunde.
Exemplo 77: provideHttpClient
Ative o HttpClient na configuração da aplicação.
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [provideHttpClient()],
};
Exemplo 78: Buscando dados com o pipe async
Mostre uma lista vinda de HTTP sem assinatura manual.
import { Component, inject } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Component({
selector: 'app-users',
standalone: true,
imports: [AsyncPipe],
template: `
@for (user of users$ | async; track user.id) {
<p>{{ user.name }}</p>
}
`,
})
export class UsersComponent {
private http = inject(HttpClient);
users$: Observable<{ id: number; name: string }[]> =
this.http.get<{ id: number; name: string }[]>('/api/users');
}
Exemplo 79: map e filter do RxJS
Transforme e guarde apenas os valores de que você precisa de um stream.
import { map, filter, of } from 'rxjs';
const prices$ = of(120, 35, 80);
prices$
.pipe(
filter((p) => p > 50),
map((p) => p * 1.07),
)
.subscribe((total) => console.log(total.toFixed(2)));
Exemplo 80: switchMap para busca em tempo real
Espere o usuário parar de digitar e então cancele as requisições antigas.
import { Component, inject } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs';
@Component({
selector: 'app-live-search',
standalone: true,
imports: [ReactiveFormsModule],
template: '<input [formControl]="query">',
})
export class LiveSearchComponent {
private http = inject(HttpClient);
query = new FormControl('', { nonNullable: true });
results$ = this.query.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap((q) => this.http.get<string[]>('/api/search', { params: { q } })),
);
}
Exemplo 81: catchError e retry
Tente a requisição que falhou de novo e depois volte para um valor seguro.
import { inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { catchError, of, retry } from 'rxjs';
const http = inject(HttpClient);
const safeList$ = http.get<string[]>('/api/tags').pipe(
retry(2),
catchError(() => of([] as string[])),
);
Exemplo 82: HTTP POST
Envie dados ao servidor com um corpo na requisição.
import { inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
export class PostService {
private http = inject(HttpClient);
createPost(title: string) {
return this.http.post<{ id: number }>('/api/posts', { title });
}
}
Tópico 12: Diretivas
Uma diretiva muda como um elemento se comporta ou se ele aparece. Uma diretiva de atributo altera um elemento; uma diretiva estrutural adiciona ou remove elementos.
Diretiva de atributo
Uma diretiva com um seletor entre colchetes, como [appHighlight], pode usar HostListener para reagir a eventos do mouse e mudar o estilo do elemento. Mantenha a lógica pequena e deixe os comportamentos grandes dentro do componente.
Diretiva estrutural e TemplateRef
Uma diretiva estrutural injeta TemplateRef e ViewContainerRef. Ela cria uma view com createEmbeddedView quando uma condição é verdadeira, e a remove quando é falsa. O *ngIf e o @if integrado do Angular funcionam dessa mesma forma.
Setter de input em diretivas
Um setter de input roda sempre que o pai envia um novo valor. Use-o para reagir diretamente a um valor, por exemplo para reconstruir uma view, sem escrever ngOnChanges.
Exemplo 83: Diretiva de atributo
Uma diretiva que altera a aparência de um elemento.
import { Directive, ElementRef, HostListener, inject, input } from '@angular/core';
@Directive({
selector: '[appHighlight]',
standalone: true,
})
export class HighlightDirective {
color = input('yellow', { alias: 'appHighlight' });
private el = inject<ElementRef<HTMLElement>>(ElementRef);
@HostListener('mouseenter') onEnter() {
this.el.nativeElement.style.backgroundColor = this.color();
}
@HostListener('mouseleave') onLeave() {
this.el.nativeElement.style.backgroundColor = '';
}
}
Exemplo 84: Diretiva estrutural
Uma diretiva que adiciona ou remove elementos da página.
import { Directive, inject, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appUnless]',
standalone: true,
})
export class UnlessDirective {
private template = inject<TemplateRef<unknown>>(TemplateRef);
private view = inject(ViewContainerRef);
@Input() set appUnless(condition: boolean) {
this.view.clear();
if (!condition) {
this.view.createEmbeddedView(this.template);
}
}
}
Exemplo 85: Setter de input
Execute código sempre que o pai enviar um novo valor.
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-tag-list',
standalone: true,
template: '<p>{{ tags.join(", ") }}</p>',
})
export class TagListComponent {
private _tags: string[] = [];
@Input()
set tags(value: string[]) {
this._tags = value.map((t) => t.trim());
}
get tags(): string[] {
return this._tags;
}
}
Tópico 13: Renderização, signals e detecção de mudanças
O Angular decide o que redesenhar por meio da detecção de mudanças. Signals e OnPush permitem controlar esse processo, para que páginas grandes continuem rápidas.
Lista com índice e valores derivados
Dentro de @for você pode usar $index, $first e $last para estilizar cada linha. Um computed signal pode filtrar uma lista, de modo que o resultado seja atualizado sempre que o texto de busca ou a lista mudar.
Detecção de mudanças OnPush
Com ChangeDetectionStrategy.OnPush, o Angular só verifica o componente quando um input muda, quando um evento acontece dentro dele ou quando um signal que ele lê muda. É um grande ganho de velocidade em páginas com muitos componentes.
Zoneless e toSignal
Uma aplicação zoneless roda sem zone.js, então a view é atualizada por causa de signals e eventos, e não por qualquer evento do navegador. toSignal converte um Observable em um signal, que você pode ler no template sem assinar.
Exemplo 86: @for com $index e $first
Use a posição de cada item dentro da repetição.
@for (step of steps; track step; let i = $index, first = $first, last = $last) {
<li [class.first]="first" [class.last]="last">{{ i + 1 }}. {{ step }}</li>
}
Exemplo 87: Filtro com computed
Mostre uma lista filtrada que é atualizada enquanto o usuário digita.
import { Component, computed, signal } from '@angular/core';
@Component({
selector: 'app-fruit-filter',
standalone: true,
template: `
<input (input)="query.set($any($event.target).value)">
@for (fruit of shown(); track fruit) {
<p>{{ fruit }}</p>
}
`,
})
export class FruitFilterComponent {
fruits = ['apple', 'banana', 'cherry'];
query = signal('');
shown = computed(() =>
this.fruits.filter((f) => f.includes(this.query().toLowerCase())),
);
}
Exemplo 88: Detecção de mudanças OnPush
Verifique o componente somente quando o input muda.
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
@Component({
selector: 'app-price-tag',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<p>{{ price }} THB</p>',
})
export class PriceTagComponent {
@Input() price = 0;
}
Exemplo 89: Detecção de mudanças zoneless
Execute o Angular sem zone.js, usando signals para atualizar a view.
import { ApplicationConfig, provideZonelessChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [provideZonelessChangeDetection()],
};
Exemplo 90: toSignal
Converta um Observable em um signal que você pode ler no template.
import { Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { interval, map } from 'rxjs';
@Component({
selector: 'app-seconds',
standalone: true,
template: '<p>Seconds: {{ seconds() }}</p>',
})
export class SecondsComponent {
seconds = toSignal(interval(1000).pipe(map((n) => n + 1)), { initialValue: 0 });
}
Tópico 14: Testes e animações
Os testes verificam componentes e serviços isoladamente. Animações mudam elementos de forma suave quando o estado deles muda.
TestBed e testes de componente
TestBed cria o componente dentro de um módulo de teste, detectChanges executa a detecção de mudanças, e o teste verifica o texto de nativeElement. Mantenha cada teste verificando um único comportamento, para que uma falha aponte para uma só causa.
HttpTestingController e fakeAsync
provideHttpClientTesting troca a rede real por uma versão falsa, então expectOne e flush dão uma resposta ao teste. fakeAsync com tick avança o tempo, para que ações adiadas possam ser testadas sem esperar.
Gatilhos de animação
Um gatilho de animação define estados, como open e closed, e as transições entre eles com o tempo. Vincule o gatilho a um valor no template, e o Angular executa a transição quando o valor muda.
Exemplo 91: Teste de componente com TestBed
Crie o componente dentro do teste e verifique o texto.
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CounterComponent } from './counter.component';
describe('CounterComponent', () => {
let fixture: ComponentFixture<CounterComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({ imports: [CounterComponent] }).compileComponents();
fixture = TestBed.createComponent(CounterComponent);
});
it('starts at zero', () => {
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toContain('0');
});
});
Exemplo 92: HttpTestingController
Teste um serviço HTTP sem um servidor real.
import { TestBed } from '@angular/core/testing';
import { provideHttpClient } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { UserService } from './user.service';
describe('UserService', () => {
it('loads users', () => {
TestBed.configureTestingModule({
providers: [provideHttpClient(), provideHttpClientTesting()],
});
const service = TestBed.inject(UserService);
const http = TestBed.inject(HttpTestingController);
service.users().subscribe((list) => expect(list.length).toBe(2));
http.expectOne('/api/users').flush([{ id: 1 }, { id: 2 }]);
http.verify();
});
});
Exemplo 93: fakeAsync e tick
Avance o tempo dentro do teste para verificar ações adiadas.
import { fakeAsync, tick } from '@angular/core/testing';
it('runs a delayed action', fakeAsync(() => {
let done = false;
setTimeout(() => (done = true), 500);
tick(500);
expect(done).toBeTrue();
}));
Exemplo 94: Gatilho de animação
Anime um elemento quando o estado dele muda.
import { Component } from '@angular/core';
import { trigger, state, style, transition, animate } from '@angular/animations';
@Component({
selector: 'app-drawer',
standalone: true,
animations: [
trigger('slide', [
state('closed', style({ height: '0px' })),
state('open', style({ height: '200px' })),
transition('closed <=> open', animate('300ms ease-in')),
]),
],
template: '<div [@slide]="isOpen ? \'open\' : \'closed\'">Content</div>',
})
export class DrawerComponent {
isOpen = false;
}
Tópico 15: Configuração, erros e deploy
Uma aplicação real precisa de configurações para cada ambiente, de um plano para erros inesperados e de um build de produção pronto para publicar.
Arquivo de environment e initializer
Guarde configurações como o endereço da API em um arquivo de environment, com valores diferentes para desenvolvimento e produção. provideAppInitializer executa código, como carregar a configuração, antes de a primeira página ser desenhada.
Erros e ErrorHandler
O Angular envia erros não capturados para o ErrorHandler. Forneça sua própria classe para registrá-los ou enviá-los a um serviço de relatório, de modo que um único lugar decida o que acontece com erros inesperados.
Build, teste e deploy
ng build --configuration production gera arquivos otimizados em dist/. ng test com --watch=false roda os testes unitários uma vez, dentro do pipeline de build. Hospede os arquivos estáticos em um servidor web ou em qualquer serviço de hospedagem estática que suporte aplicações de página única.
Exemplo 95: Configuração de environment
Guarde configurações como o endereço da API em um único arquivo.
export const environment = {
production: false,
apiUrl: 'http://localhost:3000/api',
};
Exemplo 96: ErrorHandler personalizado
Registre erros inesperados em um único lugar.
import { ErrorHandler, Injectable } from '@angular/core';
@Injectable()
export class ReportingErrorHandler implements ErrorHandler {
handleError(error: unknown): void {
console.error('Unexpected error:', error);
}
}
Exemplo 97: provideAppInitializer
Carregue a configuração antes de a aplicação iniciar.
import { ApplicationConfig, provideAppInitializer, inject } from '@angular/core';
import { ConfigService } from './config.service';
export const appConfig: ApplicationConfig = {
providers: [
provideAppInitializer(() => inject(ConfigService).load()),
],
};
Exemplo 98: Importações em um componente standalone
Registre as diretivas e pipes que o componente usa.
import { Component } from '@angular/core';
import { NgClass } from '@angular/common';
import { HighlightDirective } from './highlight.directive';
import { ShortenPipe } from './shorten.pipe';
@Component({
selector: 'app-note',
standalone: true,
imports: [NgClass, HighlightDirective, ShortenPipe],
template: '<p [ngClass]="{ bold: true }" appHighlight="pink">{{ text | shorten: 10 }}</p>',
})
export class NoteComponent {
text = 'A long note about Angular standalone components';
}
Exemplo 99: @defer por interação
Carregue uma parte da página somente depois que o usuário clicar.
<button #openBtn>Show comments</button>
@defer (on interaction(openBtn)) {
<app-comments />
} @placeholder {
<p>Comments will load after you click.</p>
}
Exemplo 100: Comandos de build e teste
Os comandos principais da CLI do Angular para o trabalho do dia a dia.
# servidor de desenvolvimento
ng serve
# build de produção (gera em dist/)
ng build --configuration production
# testes unitários
ng test --watch=false
Projeto de prática: Lista de tarefas
Construa uma pequena aplicação que use as ideias de cada seção. Escreva primeiro sozinho e depois compare com os exemplos.
1. Crie um projeto standalone e um componente TaskList com um signal que guarda o array de tarefas.
2. Adicione um formulário com um campo de texto obrigatório, e acrescente cada nova tarefa à lista com um método ligado a (ngSubmit).
3. Mostre as tarefas com @for e track, além de uma mensagem @empty quando a lista estiver vazia.
4. Adicione um computed signal que conte as tarefas não concluídas, e mostre-o no template.
5. Adicione uma rota para uma página de detalhe da tarefa que lê o id da URL com ActivatedRoute.
6. Escreva um teste de componente que verifique a mensagem de lista vazia, e depois rode ng test --watch=false.
Próximos passos
Em seguida, estude o roteador em profundidade, o gerenciamento de estado com signals ou NgRx, a renderização no servidor com Angular SSR e bibliotecas de componentes como o Angular Material. A documentação oficial em angular.dev é a melhor referência, e os exemplos aqui seguem a sintaxe que ela recomenda a partir do Angular 17.
Resumo
O Angular constrói aplicações a partir de componentes. Templates ligam dados e eventos, os blocos de controle de fluxo mostram ou repetem conteúdo, os pipes formatam valores, e inputs e outputs conectam pai e filho. Os componentes guardam seu estado em propriedades comuns, valores readonly, getters e signals. Serviços são compartilhados pela injeção de dependência, o roteamento associa URLs a páginas, os formulários validam dados, e o HttpClient com RxJS carrega informações. Testes, configuração e o build de produção completam o fluxo. A melhor forma de lembrar esses 100 exemplos é construir o projeto de lista de tarefas e mudar uma coisa de cada vez.