Angular do Zero a 100 Exemplos: Componentes, Templates, Signals, Formulários, Rotas, HTTP e Todos os Tipos de Propriedade

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

Como um componente Angular aparece na página: template e classe são compilados juntos, o Angular guarda o estado do componente, e signals e a detecção de mudanças atualizam apenas as partes da tela que mudaram
Como um componente Angular aparece na página: template e classe são compilados juntos, o Angular guarda o estado do componente, e signals e a detecção de mudanças atualizam apenas as partes da tela que mudaram

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.