Do Zero ao Profissional - Aprenda o framework da Google para criar aplicações web modernas em qualquer sistema operacional. Mais de 2.000 linhas de conteúdo detalhado! Treinamento Angular Completo - Do Zero ao Profissional (Windows, Mac e Linux)
🅰️ Material de Estudo • 2026

🅰️ Treinamento Angular Completo

Do Zero ao Profissional - Aprenda o framework da Google para criar aplicações web modernas em qualquer sistema operacional. Mais de 2.000 linhas de conteúdo detalhado!

⏱️ Duração: 12-15 horas 📚 15 Módulos ✍️ 35+ Exercícios 💡 50+ Dicas 📄 2.500+ linhas 🪟 Windows 🍎 Mac 🐧 Linux ✅ Multiplataforma

📌 Neste Treinamento você vai aprender (15 Módulos Completos):

1 Introdução ao Angular
2 Setup para Windows/Mac/Linux
3 Fundamentos TypeScript
4 Componentes e Templates
5 Diretivas Estruturais e de Atributo
6 Serviços e Injeção de Dependência
7 Roteamento Avançado
8 Formulários Reativos e Template-driven
9 HTTP Client e APIs REST
10 RxJS e Programação Reativa
11 Pipes e Transformação de Dados
12 Testes Unitários e E2E
13 Build e Deploy
14 35+ Exercícios Práticos
15 Glossário Técnico
1

Módulo 1: Introdução ao Angular

🅰️ Angular 📘 TypeScript ⚙️ Fundamentos

📖 O que é Angular?

Angular é um framework de desenvolvimento front-end mantido pela Google, usado para criar aplicações web dinâmicas, escaláveis e de alta performance. Foi lançado em 2016 como uma reescrita completa do AngularJS (versão 1.x), trazendo uma arquitetura baseada em componentes e utilizando TypeScript como linguagem principal.

🔍 Angular vs AngularJS - Qual a diferença?

  • AngularJS (v1.x): Framework JavaScript, baseado em controllers e $scope, arquitetura MVC
  • Angular (2+): Framework TypeScript, baseado em componentes, mais performático, mobile-ready
  • Versão atual: Angular 19 (2025/2026) - suporte LTS (Long Term Support) garantido pela Google
  • Principais melhorias: Ivy renderer, standalone components, signals, melhor performance

💡 História do Angular

O Angular foi criado por Miško Hevery e Adam Abrons em 2009 como um projeto paralelo. Em 2010, a Google começou a patrocinar o projeto. Em 2016, uma versão completamente reescrita foi lançada como Angular 2, marcando uma mudança radical na arquitetura. Atualmente, o Angular é um dos frameworks mais utilizados no mundo, com milhões de desenvolvedores e milhares de aplicações em produção.

🎯 Por que usar Angular?

✅ TypeScript

Tipagem estática, melhor experiência de desenvolvimento, código mais seguro e fácil de manter.

✅ Componentes

Arquitetura baseada em componentes reutilizáveis, facilitando a organização do código.

✅ Injeção de Dependência

Framework nativo para gerenciar dependências, promovendo desacoplamento e testabilidade.

✅ CLI Poderosa

Ferramenta de linha de comando que agiliza o desenvolvimento, geração de código e build.

✅ Ecossistema Completo

Router, HTTP Client, Forms, Animations, PWA, SSR, tudo incluso no framework.

✅ Suporte Google

Framework empresarial com suporte de longo prazo, utilizado por grandes empresas mundialmente.

🏗️ Arquitetura Angular

┌─────────────────────────────────────────────────────────────────┐
│                      Módulos (NgModule)                          │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                    Componentes                             │  │
│  │  ┌─────────────────┐    ┌─────────────────┐              │  │
│  │  │   Template      │    │    Classe       │              │  │
│  │  │   (HTML)        │◄──►│  (TypeScript)   │              │  │
│  │  └─────────────────┘    └─────────────────┘              │  │
│  │  ┌─────────────────┐    ┌─────────────────┐              │  │
│  │  │   Estilos       │    │   Metadados     │              │  │
│  │  │   (CSS/SCSS)    │    │  @Component     │              │  │
│  │  └─────────────────┘    └─────────────────┘              │  │
│  └───────────────────────────────────────────────────────────┘  │
│                                                                 │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐         │
│  │  Serviços    │  │  Diretivas   │  │   Pipes      │         │
│  │  (Services)  │  │ (Directives) │  │  (Filters)   │         │
│  └──────────────┘  └──────────────┘  └──────────────┘         │
│                                                                 │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐         │
│  │   Router     │  │  HTTPClient  │  │  Animations  │         │
│  └──────────────┘  └──────────────┘  └──────────────┘         │
└─────────────────────────────────────────────────────────────────┘

📌 Componentes Standalone (Angular 14+)

A partir do Angular 14, é possível criar componentes sem a necessidade de NgModules, simplificando a estrutura e reduzindo boilerplate. Os componentes standalone são a abordagem recomendada para novos projetos.

🔄 Ciclo de Vida do Componente

FaseMétodoDescrição
Criaçãoconstructor()Primeiro a ser executado, ideal para injeção de dependências
InicializaçãongOnInit()Executado após a criação, ideal para carregar dados iniciais
VerificaçãongDoCheck()Executado a cada ciclo de detecção de mudanças
ConteúdongAfterContentInit()Após projetar conteúdo no componente
VisualizaçãongAfterViewInit()Após inicializar a view do componente
DestruiçãongOnDestroy()Limpeza antes de remover o componente
2

Módulo 2: Setup do Ambiente - Windows, Mac e Linux

⚙️ CLI 🪟 Windows 🍎 Mac 🐧 Linux 🔧 Configuração

📋 Pré-requisitos (Todos os Sistemas Operacionais)

Antes de começar a programar em Angular, você precisa ter instalado no seu computador:

  • Node.js (versão LTS 18.x, 20.x ou superior) - nodejs.org
  • npm (Node Package Manager) - já incluso na instalação do Node.js
  • Visual Studio Code (recomendado) - code.visualstudio.com
  • Git (opcional, mas recomendado para versionamento) - git-scm.com

⚠️ Verifique sua versão do Node.js

Angular requer Node.js versão 18.10.0 ou superior. Para verificar sua versão, abra o terminal e digite:

node --version

Se a versão for inferior, atualize o Node.js antes de prosseguir.

📥 Instalação do Node.js por Sistema Operacional

🪟 Windows
🍎 macOS
🐧 Linux (Ubuntu/Debian)
🐧 Linux (Fedora/RHEL)
🔹 MÉTODO 1: Instalador Oficial (Recomendado para iniciantes)
1. Acesse https://nodejs.org
2. Baixe a versão LTS (arquivo .msi - Windows Installer)
3. Execute o instalador baixado
4. Clique em "Next" até finalizar (mantenha as opções padrão)
5. Reinicie o terminal após a instalação

🔹 MÉTODO 2: Via Chocolatey (para usuários avançados)
choco install nodejs-lts

🔹 MÉTODO 3: Via Winget (Windows 11 / Windows 10 atualizado)
winget install OpenJS.NodeJS.LTS

🔹 Verificar instalação:
node --version
npm --version

⚠️ Problema comum no Windows: Política de Execução

Se ao tentar executar comandos npm/ng aparecer o erro "cannot be loaded because running scripts is disabled", execute no PowerShell como Administrador:

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

Depois, feche e abra o terminal novamente.

💡 Dica Windows: WSL2 (Windows Subsystem for Linux)

Para uma experiência mais próxima do Linux, você pode instalar o WSL2 e rodar o Angular dentro de uma distribuição Ubuntu. Isso é especialmente útil se você trabalha com equipes que usam Linux/macOS.

🔹 MÉTODO 1: Instalador Oficial (mais simples)
1. Acesse https://nodejs.org
2. Baixe a versão LTS para macOS (arquivo .pkg)
3. Execute o instalador e siga as instruções
4. Reinicie o terminal

🔹 MÉTODO 2: Via Homebrew (recomendado para desenvolvedores)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install node

🔹 MÉTODO 3: Via nvm (Node Version Manager) - gerencia múltiplas versões
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install --lts
nvm use --lts

🔹 Verificar instalação:
node --version
npm --version

🍎 Dica para Mac: Permissões

Se encontrar problemas de permissão ao instalar pacotes globais, use sudo antes do comando:

sudo npm install -g @angular/cli

Ou ajuste as permissões da pasta global do npm:

sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}
🔹 Ubuntu/Debian (via repositório oficial)
sudo apt update
sudo apt install nodejs npm -y

🔹 Ubuntu/Debian (NodeSource - versão mais recente)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

🔹 Via nvm (recomendado - funciona em todas distribuições)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
# Reinicie o terminal
nvm install --lts
nvm use --lts

🔹 Verificar instalação:
node --version
npm --version

🐧 Dica para Linux: Permissões globais

Para evitar usar sudo ao instalar pacotes npm globalmente, configure o npm para usar um diretório pessoal:

mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
🔹 Fedora / RHEL / CentOS
sudo dnf install nodejs npm -y

🔹 Para versão mais recente (NodeSource)
curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
sudo dnf install nodejs -y

🔹 Arch Linux / Manjaro
sudo pacman -S nodejs npm

🔹 Via nvm (recomendado)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install --lts

🔹 Verificar instalação:
node --version
npm --version

🅰️ Instalando Angular CLI (Comum a todos OS)

# Instalar Angular CLI globalmente (mesmo comando em todos OS)
npm install -g @angular/cli

# Verificar instalação
ng version

# Saída esperada:
# Angular CLI: 19.0.0
# Node: 20.10.0
# Package Manager: npm 10.2.0
# OS: (windows/mac/linux)

💡 Curiosidade: Angular CLI

O Angular CLI (Command Line Interface) foi criado para padronizar e agilizar o desenvolvimento. Ele gerencia desde a criação do projeto até o build para produção, além de gerar componentes, serviços, módulos e outras estruturas com um único comando. É uma das ferramentas mais poderosas do ecossistema Angular!

🚀 Criando seu primeiro projeto Angular

# Criar novo projeto (mesmo comando em todos OS)
ng meu-primeiro-projeto

# Durante a criação, responda:
# ✅ Would you like to add Angular routing? Yes (para projetos com múltiplas páginas)
# ✅ Which stylesheet format? CSS (ou SCSS/Sass se preferir)
# ✅ Do you want to enable Server-Side Rendering? No (para começar)

# Entrar na pasta do projeto
cd meu-primeiro-projeto

# Executar o servidor de desenvolvimento
ng serve

# Abrir no navegador: http://localhost:4200

# Para parar o servidor: Ctrl + C

🎯 Dica: ng serve --open

Use ng serve --open (ou ng serve -o) para abrir automaticamente o navegador quando o servidor iniciar.

📁 Estrutura do Projeto Angular

meu-primeiro-projeto/
├── .angular/                    # Configurações do Angular
├── .vscode/                     # Configurações do VS Code
├── node_modules/                # Dependências do projeto (não versionar!)
├── public/                      # Arquivos públicos (favicon, assets)
├── src/                         # Código fonte da aplicação
│   ├── app/                     # Componentes, serviços e módulos
│   │   ├── app.component.css    # Estilos do componente principal
│   │   ├── app.component.html   # Template do componente principal
│   │   ├── app.component.spec.ts # Testes do componente principal
│   │   ├── app.component.ts     # Lógica do componente principal
│   │   ├── app.config.ts        # Configuração da aplicação (standalone)
│   │   └── app.routes.ts        # Configuração de rotas
│   ├── assets/                  # Imagens, fonts, etc
│   ├── index.html               # Página principal
│   ├── main.ts                  # Ponto de entrada da aplicação
│   └── styles.css               # Estilos globais
├── .editorconfig                # Configuração do editor
├── .gitignore                   # Arquivos ignorados pelo Git
├── angular.json                 # Configuração do workspace Angular
├── package.json                 # Dependências e scripts
├── tsconfig.json                # Configuração do TypeScript
└── README.md                    # Documentação do projeto

🔧 Comandos Angular CLI Essenciais

ng new nome-projeto
ng serve
ng generate component nome
ng generate service nome
ng generate module nome
ng generate pipe nome
ng generate directive nome
ng build
ng test
ng e2e
3

Módulo 3: Fundamentos TypeScript

📘 TypeScript 💻 Código

📘 O que é TypeScript?

TypeScript é um superset do JavaScript que adiciona tipagem estática e recursos modernos como interfaces, generics, decorators e muito mais. Angular é escrito inteiramente em TypeScript, e entender seus conceitos é fundamental para se tornar um desenvolvedor Angular proficiente.

🔍 JavaScript vs TypeScript - Exemplo Prático

// ========== JAVASCRIPT (sem tipos) ==========
function soma(a, b) {
    return a + b;
}
console.log(soma(5, "10"));  // Retorna "510" - Comportamento inesperado!

// ========== TYPESCRIPT (com tipos) ==========
function soma(a: number, b: number): number {
    return a + b;
}
console.log(soma(5, "10"));  // ❌ ERRO: Argumento do tipo 'string' não é atribuível a 'number'

// Isso detecta o erro ANTES de executar o código!

🔤 Tipos Básicos no TypeScript

// ========== Tipos Primitivos ==========
let nome: string = "João Silva";
let idade: number = 25;
let ativo: boolean = true;
let dados: any = "pode ser qualquer coisa";  // Evitar usar, quebra a tipagem

// ========== Arrays ==========
let numeros: number[] = [1, 2, 3, 4, 5];
let nomes: Array<string> = ["Ana", "Pedro", "Maria"];

// ========== Tuplas ==========
let pessoa: [string, number] = ["João", 30];

// ========== Enums ==========
enum Status {
    Pendente = "PENDENTE",
    EmAndamento = "EM_ANDAMENTO",
    Concluido = "CONCLUIDO"
}
let meuStatus: Status = Status.EmAndamento;

// ========== Union Types ==========
let id: string | number;
id = "ABC123";
id = 456;  // Também válido

// ========== Type Aliases ==========
type ID = string | number;
type UsuarioType = {
    id: ID;
    nome: string;
    email: string;
};

📦 Interfaces e Classes

// ========== Interfaces ==========
interface Produto {
    id: number;
    nome: string;
    preco: number;
    categoria?: string;  // Opcional
    readonly codigo: string;  // Somente leitura
}

const produto: Produto = {
    id: 1,
    nome: "Notebook",
    preco: 2500,
    codigo: "NB-001"
};

// ========== Classes ==========
class Usuario {
    // Modificadores de acesso: public, private, protected
    private _id: number;
    public nome: string;
    protected email: string;
    
    constructor(id: number, nome: string, email: string) {
        this._id = id;
        this.nome = nome;
        this.email = email;
    }
    
    // Getter
    get id(): number {
        return this._id;
    }
    
    // Método
    exibirInfo(): string {
        return `${this.nome} (${this.email})`;
    }
    
    // Método estático
    static criarAdmin(): Usuario {
        return new Usuario(0, "Admin", "admin@empresa.com");
    }
}

// Herança
class Cliente extends Usuario {
    private pontos: number = 0;
    
    adicionarPontos(valor: number): void {
        this.pontos += valor;
    }
    
    // Sobrescrita de método
    exibirInfo(): string {
        return `${super.exibirInfo()} - Pontos: ${this.pontos}`;
    }
}

⚙️ Generics e Utilitários

// ========== Generics ==========
function identidade<T>(valor: T): T {
    return valor;
}

let texto = identidade<string>("Olá");
let numero = identidade<number>(42);

// ========== Classe Genérica ==========
class Repositorio<T> {
    private itens: T[] = [];
    
    adicionar(item: T): void {
        this.itens.push(item);
    }
    
    listar(): T[] {
        return [...this.itens];
    }
    
    buscar(predicado: (item: T) => boolean): T | undefined {
        return this.itens.find(predicado);
    }
}

const repoUsuarios = new Repositorio<Usuario>();
repoUsuarios.adicionar(new Usuario(1, "João", "joao@email.com"));

// ========== Tipos Utilitários ==========
interface Config {
    host: string;
    port: number;
    ssl: boolean;
}

// Partial - todos os campos opcionais
type ConfigParcial = Partial<Config>;

// Required - todos os campos obrigatórios
type ConfigObrigatoria = Required<Config>;

// Pick - seleciona campos específicos
type ConfigBasica = Pick<Config, "host" | "port">;

// Omit - omite campos específicos
type ConfigSemSSL = Omit<Config, "ssl">;

🎨 Decorators (Essenciais para Angular)

// Decorators são funções que adicionam metadados a classes, métodos ou propriedades
// Angular usa decorators extensivamente:

// @Component - Define um componente Angular
@Component({
    selector: 'app-meu-componente',
    templateUrl: './meu-componente.component.html',
    styleUrls: ['./meu-componente.component.css']
})
export class MeuComponente { }

// @Injectable - Define um serviço injetável
@Injectable({
    providedIn: 'root'
})
export class MeuServico { }

// @Input - Propriedade que recebe dados do componente pai
@Input() titulo: string;

// @Output - Evento que emite dados para o componente pai
@Output() evento = new EventEmitter<string>();

// @ViewChild - Referência a um elemento filho no template
@ViewChild('meuElemento') elemento: ElementRef;
4

Módulo 4: Componentes e Templates

🧩 Componentes 🅰️ Core

🧩 Criando Componentes

# Usando Angular CLI (recomendado)
ng generate component MeuComponente
# ou forma abreviada
ng g c MeuComponente

# Para criar com arquivos específicos
ng g c MeuComponente --flat          # Não cria pasta separada
ng g c MeuComponente --inline-template # Template inline
ng g c MeuComponente --inline-style    # Estilos inline
ng g c MeuComponente --skip-tests      # Não cria arquivo de teste

📄 Estrutura de um Componente

// meu-componente.component.ts
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';

@Component({
    selector: 'app-meu-componente',      // Nome da tag HTML
    templateUrl: './meu-componente.component.html',  // Template externo
    styleUrls: ['./meu-componente.component.css'],   // Estilos externos
    // Ou template/estilos inline:
    // template: `<h1>Olá Mundo!</h1>`,
    // styles: [`h1 { color: red; }`]
})
export class MeuComponenteComponent implements OnInit {
    // Propriedades do componente
    titulo: string = "Meu Primeiro Componente!";
    contador: number = 0;
    
    // Input - recebe dados do componente pai
    @Input() nomeUsuario: string = "";
    
    // Output - envia eventos para o componente pai
    @Output() contadorAlterado = new EventEmitter<number>();
    
    // Construtor - injeta dependências
    constructor() {
        console.log("Componente criado!");
    }
    
    // Lifecycle hook - executado após a inicialização
    ngOnInit(): void {
        console.log("Componente inicializado!");
    }
    
    // Métodos do componente
    incrementar(): void {
        this.contador++;
        this.contadorAlterado.emit(this.contador);
    }
    
    decrementar(): void {
        this.contador--;
        this.contadorAlterado.emit(this.contador);
    }
    
    resetar(): void {
        this.contador = 0;
        this.contadorAlterado.emit(this.contador);
    }
}

🎨 Template HTML

<!-- meu-componente.component.html -->
<div class="componente-container">
    <h2>{{ titulo }}</h2>
    
    <div *ngIf="nomeUsuario">
        <p>Bem-vindo(a), {{ nomeUsuario }}!</p>
    </div>
    
    <div class="contador-container">
        <p>Contador atual: <strong>{{ contador }}</strong></p>
        <div class="botoes">
            <button (click)="decrementar()" [disabled]="contador === 0">-</button>
            <button (click)="resetar()">Reset</button>
            <button (click)="incrementar()">+</button>
        </div>
    </div>
</div>

🔗 Data Binding (Vinculação de Dados)

📌 Interpolação

Sintaxe: {{ expressao }}

<h1>Olá, {{ usuario.nome }}!</h1>
<p>Você tem {{ idade }} anos.</p>

📌 Property Binding

Sintaxe: [propriedade]="expressao"

<img [src]="imagemUrl">
<button [disabled]="isDesabilitado">Clique</button>

📌 Event Binding

Sintaxe: (evento)="metodo()"

<button (click)="salvar()">Salvar</button>
<input (keyup.enter)="buscar($event)">

📌 Two-way Binding

Sintaxe: [(ngModel)]="variavel"

<input [(ngModel)]="nome">
<p>Olá, {{ nome }}!</p>

⚠️ Requer importar FormsModule no módulo ou componente standalone.

🔄 Lifecycle Hooks

HookTimingUso Comum
ngOnChanges()Quando um input property mudaReagir a mudanças em @Input()
ngOnInit()Após o primeiro ngOnChangesInicializar dados, chamar APIs
ngDoCheck()A cada ciclo de detecção de mudançasDetecção customizada
ngAfterContentInit()Após projetar conteúdoInicializar conteúdo projetado
ngAfterContentChecked()A cada verificação de conteúdoLógica após verificação
ngAfterViewInit()Após inicializar a viewAcessar elementos DOM
ngAfterViewChecked()A cada verificação da viewLógica após verificação
ngOnDestroy()Antes de destruir o componenteLimpar recursos, unsubscribe
// Exemplo prático de lifecycle hooks
export class MeuComponente implements OnInit, OnChanges, OnDestroy {
    
    @Input() dados: any;
    
    ngOnChanges(changes: SimpleChanges): void {
        console.log('Input changed:', changes);
    }
    
    ngOnInit(): void {
        console.log('Componente inicializado');
        this.carregarDados();
    }
    
    ngOnDestroy(): void {
        console.log('Componente destruído');
        // Limpar subscriptions
        this.subscription.unsubscribe();
    }
}
5

Módulo 5: Diretivas Estruturais e de Atributo

🅰️ Diretivas 💻 Templates

🔧 Diretivas Estruturais

Diretivas estruturais modificam a estrutura do DOM (adicionam/removem elementos). São identificadas pelo asterisco (*).

// ========== *ngIf - Renderização Condicional ==========
<div *ngIf="usuarioLogado; else naoLogado">
    <p>Bem-vindo, {{ usuario.nome }}!</p>
</div>
<ng-template #naoLogado>
    <p>Faça login para continuar</p>
</ng-template>

// Versão com else if (Angular 17+)
@if (usuarioLogado) {
    <p>Bem-vindo, {{ usuario.nome }}!</p>
} @else {
    <p>Faça login</p>
}

// ========== *ngFor - Repetição ==========
<ul>
    <li *ngFor="let item of lista; let i = index; let primeiro = first; let ultimo = last">
        {{ i + 1 }} - {{ item.nome }}
        <span *ngIf="primeiro"> (Primeiro)</span>
        <span *ngIf="ultimo"> (Último)</span>
    </li>
</ul>

// Versão Angular 17+ (@for)
@for (item of lista; track item.id; let i = $index) {
    <li>{{ i + 1 }} - {{ item.nome }}</li>
} @empty {
    <li>Nenhum item encontrado</li>
}

// ========== *ngSwitch - Múltiplas Condições ==========
<div [ngSwitch]="status">
    <p *ngSwitchCase="'ativo'">✅ Status: Ativo</p>
    <p *ngSwitchCase="'inativo'">⭕ Status: Inativo</p>
    <p *ngSwitchCase="'pendente'">⏳ Status: Pendente</p>
    <p *ngSwitchDefault>❓ Status desconhecido</p>
</div>

🎨 Diretivas de Atributo

Diretivas de atributo alteram a aparência ou comportamento de um elemento existente.

// ========== ngClass - Classes CSS Condicionais ==========
<div [ngClass]="{
    'ativo': isAtivo,
    'inativo': !isAtivo,
    'destaque': isDestaque
}">
    Conteúdo com classes dinâmicas
</div>

// Também pode usar string ou array
<div [ngClass]="'classe1 classe2'"></div>
<div [ngClass]="['classe1', 'classe2']"></div>

// ========== ngStyle - Estilos Inline Condicionais ==========
<div [ngStyle]="{
    'color': corTexto,
    'font-size': tamanhoFonte + 'px',
    'background-color': isDestaque ? '#ff0' : '#fff'
}">
    Texto estilizado dinamicamente
</div>

// ========== ngModel - Two-way Binding (FormsModule) ==========
<input [(ngModel)]="nome" placeholder="Digite seu nome">
<p>Olá, {{ nome }}!</p>

🛠️ Criando Diretivas Customizadas

// gerar diretiva: ng g directive destaque
import { Directive, ElementRef, Input, HostListener } from '@angular/core';

@Directive({
    selector: '[appDestaque]',
    standalone: true
})
export class DestaqueDirective {
    
    @Input('appDestaque') cor: string = 'yellow';
    
    constructor(private el: ElementRef) { }
    
    @HostListener('mouseenter') onMouseEnter() {
        this.destacar(this.cor);
    }
    
    @HostListener('mouseleave') onMouseLeave() {
        this.destacar(null);
    }
    
    private destacar(cor: string | null) {
        this.el.nativeElement.style.backgroundColor = cor;
    }
}

// Uso no template:
<p appDestaque="lightblue">Passe o mouse aqui!</p>
6

Módulo 6: Serviços e Injeção de Dependência

🔌 Serviços 📘 DI

🔌 O que são Serviços?

Serviços são classes que concentram lógica de negócio, compartilhamento de dados e comunicação com APIs. Eles são injetados nos componentes através do sistema de Injeção de Dependência (DI) do Angular.

✅ Vantagens dos Serviços:

  • Separação de responsabilidades (componentes focam na view)
  • Reutilização de código entre componentes
  • Facilidade para testes unitários
  • Singleton por escopo (compartilhamento de estado)

📄 Criando um Serviço

# Gerar serviço
ng generate service dados
# ou
ng g s dados

// dados.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';

export interface Usuario {
    id: number;
    nome: string;
    email: string;
}

@Injectable({
    providedIn: 'root'  // Disponível em toda aplicação (singleton)
})
export class DadosService {
    
    private apiUrl = 'https://jsonplaceholder.typicode.com/users';
    private usuariosSubject = new BehaviorSubject<Usuario[]>([]);
    public usuarios$ = this.usuariosSubject.asObservable();
    
    constructor(private http: HttpClient) { }
    
    // Método que retorna Observable
    listarUsuarios(): Observable<Usuario[]> {
        return this.http.get<Usuario[]>(this.apiUrl);
    }
    
    // Método que atualiza o BehaviorSubject
    atualizarUsuarios(usuarios: Usuario[]): void {
        this.usuariosSubject.next(usuarios);
    }
    
    // Método com callback
    buscarUsuario(id: number, callback: (usuario: Usuario) => void): void {
        this.http.get<Usuario>(`${this.apiUrl}/${id}`).subscribe({
            next: (usuario) => callback(usuario),
            error: (err) => console.error('Erro:', err)
        });
    }
    
    // Método com Promise
    async criarUsuario(usuario: Partial<Usuario>): Promise<Usuario> {
        try {
            return await this.http.post<Usuario>(this.apiUrl, usuario).toPromise();
        } catch (error) {
            throw new Error('Erro ao criar usuário');
        }
    }
}

💉 Injeção de Dependência

// Injetando serviço no componente
import { Component, OnInit } from '@angular/core';
import { DadosService, Usuario } from './dados.service';

@Component({
    selector: 'app-usuarios',
    template: `
        <div *ngIf="carregando">Carregando...</div>
        <ul>
            <li *ngFor="let usuario of usuarios">
                {{ usuario.nome }} - {{ usuario.email }}
            </li>
        </ul>
    `
})
export class UsuariosComponent implements OnInit {
    usuarios: Usuario[] = [];
    carregando = false;
    
    // Injeção via construtor
    constructor(private dadosService: DadosService) { }
    
    ngOnInit() {
        this.carregando = true;
        this.dadosService.listarUsuarios().subscribe({
            next: (dados) => {
                this.usuarios = dados;
                this.carregando = false;
            },
            error: (err) => {
                console.error(err);
                this.carregando = false;
            }
        });
    }
}

🏭 Hierarquia de Injeção

providedIn: 'root'

Singleton global - uma única instância para toda aplicação. Ideal para serviços que compartilham dados globalmente.

providedIn: 'platform'

Compartilhado entre múltiplas aplicações na mesma página. Raramente usado.

providedIn: 'any'

Uma instância por módulo que importa o serviço.

providers no componente

Uma instância por componente (cada instância do componente tem seu próprio serviço).

7

Módulo 7: Roteamento Avançado

🛣️ Router 🅰️ Navegação

🛣️ Configuração de Rotas

// app.routes.ts (Angular standalone)
import { Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { SobreComponent } from './sobre/sobre.component';
import { ContatoComponent } from './contato/contato.component';
import { ProdutoDetalheComponent } from './produto-detalhe/produto-detalhe.component';
import { AuthGuard } from './guards/auth.guard';

export const routes: Routes = [
    { path: '', component: HomeComponent },
    { path: 'sobre', component: SobreComponent },
    { path: 'contato', component: ContatoComponent },
    { 
        path: 'produtos', 
        loadChildren: () => import('./produtos/produtos.routes').then(m => m.produtosRoutes)
    },
    { 
        path: 'admin', 
        canActivate: [AuthGuard],
        loadChildren: () => import('./admin/admin.routes').then(m => m.adminRoutes)
    },
    { path: 'produto/:id', component: ProdutoDetalheComponent },
    { path: 'busca', component: BuscaComponent },
    { path: '**', redirectTo: '' }  // Rota curinga (404)
];

🎯 Template com Rotas

<!-- app.component.html -->
<nav>
    <a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
        Home
    </a>
    <a routerLink="/sobre" routerLinkActive="active">Sobre</a>
    <a routerLink="/contato" routerLinkActive="active">Contato</a>
    <a [routerLink]="['/produto', produtoId]">Produto</a>
</nav>

<router-outlet></router-outlet>

🔐 Route Guards (Proteção de Rotas)

// auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
    
    constructor(private router: Router, private authService: AuthService) { }
    
    canActivate(
        route: ActivatedRouteSnapshot,
        state: RouterStateSnapshot
    ): boolean {
        if (this.authService.isAuthenticated()) {
            return true;
        }
        
        // Redirecionar para login com returnUrl
        this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
        return false;
    }
}

// Uso no componente
import { ActivatedRoute, Router } from '@angular/router';

export class MeuComponente {
    constructor(
        private route: ActivatedRoute,
        private router: Router
    ) { }
    
    navegarParaDetalhe(id: number) {
        this.router.navigate(['/produto', id], {
            queryParams: { origem: 'lista', pagina: 1 },
            fragment: 'comentarios'
        });
    }
    
    obterParametros() {
        // Parâmetros da rota
        const id = this.route.snapshot.params['id'];
        
        // Query params
        this.route.queryParams.subscribe(params => {
            console.log(params['origem']);
        });
    }
}
8

Módulo 8: Formulários Reativos e Template-driven

📝 Forms 📘 Reactive

📝 Formulários Reativos (Reactive Forms)

Abordagem baseada em código, mais robusta e escalável. Recomendada para formulários complexos.

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, Validators, AbstractControl } from '@angular/forms';

@Component({
    selector: 'app-root',
    template: `
        <form [formGroup]="cadastroForm" (ngSubmit)="onSubmit()">
            <div>
                <label>Nome:</label>
                <input formControlName="nome">
                <div *ngIf="nome?.invalid && nome?.touched">
                    <small *ngIf="nome?.errors?.['required']">Nome é obrigatório</small>
                    <small *ngIf="nome?.errors?.['minlength']">
                        Mínimo de 3 caracteres
                    </small>
                </div>
            </div>
            
            <div>
                <label>Email:</label>
                <input formControlName="email" type="email">
                <div *ngIf="email?.invalid && email?.touched">
                    <small *ngIf="email?.errors?.['required']">Email é obrigatório</small>
                    <small *ngIf="email?.errors?.['email']">Email inválido</small>
                </div>
            </div>
            
            <div>
                <label>Idade:</label>
                <input formControlName="idade" type="number">
            </div>
            
            <div formArrayName="telefones">
                <h4>Telefones</h4>
                <div *ngFor="let tel of telefones.controls; let i=index">
                    <input [formControlName]="i" placeholder="Telefone {{i+1}}">
                    <button type="button" (click)="removerTelefone(i)">Remover</button>
                </div>
                <button type="button" (click)="adicionarTelefone()">+ Adicionar Telefone</button>
            </div>
            
            <button type="submit" [disabled]="cadastroForm.invalid">Cadastrar</button>
        </form>
    `
})
export class AppComponent implements OnInit {
    cadastroForm: FormGroup;
    
    constructor(private fb: FormBuilder) { }
    
    ngOnInit() {
        this.cadastroForm = this.fb.group({
            nome: ['', [Validators.required, Validators.minLength(3)]],
            email: ['', [Validators.required, Validators.email]],
            idade: [null, [Validators.min(18), Validators.max(100)]],
            telefones: this.fb.array([])
        });
    }
    
    get nome() { return this.cadastroForm.get('nome'); }
    get email() { return this.cadastroForm.get('email'); }
    get telefones() { return this.cadastroForm.get('telefones') as FormArray; }
    
    adicionarTelefone() {
        this.telefones.push(this.fb.control('', Validators.pattern(/^\d{10,11}$/)));
    }
    
    removerTelefone(index: number) {
        this.telefones.removeAt(index);
    }
    
    onSubmit() {
        if (this.cadastroForm.valid) {
            console.log(this.cadastroForm.value);
        }
    }
}

📄 Validações Customizadas

// validators/custom.validators.ts
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';

export class CustomValidators {
    
    static senhaForte(): ValidatorFn {
        return (control: AbstractControl): ValidationErrors | null => {
            const value = control.value;
            if (!value) return null;
            
            const hasUpperCase = /[A-Z]/.test(value);
            const hasLowerCase = /[a-z]/.test(value);
            const hasNumber = /[0-9]/.test(value);
            const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(value);
            const isValidLength = value.length >= 8;
            
            const passwordValid = hasUpperCase && hasLowerCase && hasNumber && hasSpecialChar && isValidLength;
            
            return !passwordValid ? { senhaForte: true } : null;
        };
    }
    
    static compararSenhas(senhaControlName: string, confirmarSenhaControlName: string): ValidatorFn {
        return (group: AbstractControl): ValidationErrors | null => {
            const senha = group.get(senhaControlName)?.value;
            const confirmar = group.get(confirmarSenhaControlName)?.value;
            
            return senha === confirmar ? null : { senhasDiferentes: true };
        };
    }
    
    static cpfValido(): ValidatorFn {
        return (control: AbstractControl): ValidationErrors | null => {
            const cpf = control.value;
            if (!cpf) return null;
            
            // Lógica de validação de CPF
            const isValid = this.validarCPF(cpf);
            
            return isValid ? null : { cpfInvalido: true };
        };
    }
    
    private static validarCPF(cpf: string): boolean {
        // Implementação da validação de CPF
        return true;
    }
}
9

Módulo 9: HTTP Client e APIs REST

🌐 HTTP 📡 RxJS

🌐 Configurando HTTP Client

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withInterceptors, withFetch } from '@angular/common/http';
import { authInterceptor } from './interceptors/auth.interceptor';

export const appConfig: ApplicationConfig = {
    providers: [
        provideHttpClient(
            withFetch(),  // Usar Fetch API (mais moderno)
            withInterceptors([authInterceptor])  // Interceptors
        )
    ]
};

📡 Serviço para Consumir API

// api.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
import { Observable, throwError, retry, catchError, timeout, map } from 'rxjs';

export interface Usuario {
    id: number;
    name: string;
    email: string;
    username: string;
}

@Injectable({ providedIn: 'root' })
export class ApiService {
    private baseUrl = 'https://jsonplaceholder.typicode.com';
    
    constructor(private http: HttpClient) { }
    
    // GET com parâmetros
    getUsuarios(limite?: number, pagina?: number): Observable<Usuario[]> {
        let params = new HttpParams();
        if (limite) params = params.set('_limit', limite.toString());
        if (pagina) params = params.set('_page', pagina.toString());
        
        return this.http.get<Usuario[]>(`${this.baseUrl}/users`, { params })
            .pipe(
                retry(3),  // Tentar novamente em caso de erro
                timeout(10000),  // Timeout de 10 segundos
                catchError(this.handleError)
            );
    }
    
    // GET por ID
    getUsuario(id: number): Observable<Usuario> {
        return this.http.get<Usuario>(`${this.baseUrl}/users/${id}`)
            .pipe(
                map(usuario => ({
                    ...usuario,
                    name: usuario.name.toUpperCase()
                })),
                catchError(this.handleError)
            );
    }
    
    // POST - Criar recurso
    criarUsuario(usuario: Partial<Usuario>): Observable<Usuario> {
        const headers = new HttpHeaders({
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + localStorage.getItem('token')
        });
        
        return this.http.post<Usuario>(`${this.baseUrl}/users`, usuario, { headers })
            .pipe(catchError(this.handleError));
    }
    
    // PUT - Atualizar completo
    atualizarUsuario(id: number, usuario: Usuario): Observable<Usuario> {
        return this.http.put<Usuario>(`${this.baseUrl}/users/${id}`, usuario)
            .pipe(catchError(this.handleError));
    }
    
    // PATCH - Atualizar parcial
    patchUsuario(id: number, changes: Partial<Usuario>): Observable<Usuario> {
        return this.http.patch<Usuario>(`${this.baseUrl}/users/${id}`, changes)
            .pipe(catchError(this.handleError));
    }
    
    // DELETE
    deletarUsuario(id: number): Observable<void> {
        return this.http.delete<void>(`${this.baseUrl}/users/${id}`)
            .pipe(catchError(this.handleError));
    }
    
    // Upload de arquivo
    uploadArquivo(arquivo: File): Observable<any> {
        const formData = new FormData();
        formData.append('arquivo', arquivo);
        
        return this.http.post(`${this.baseUrl}/upload`, formData)
            .pipe(catchError(this.handleError));
    }
    
    // Tratamento de erros
    private handleError(error: any) {
        let errorMessage = 'Ocorreu um erro desconhecido';
        
        if (error.error instanceof ErrorEvent) {
            // Erro do lado do cliente
            errorMessage = `Erro: ${error.error.message}`;
        } else {
            // Erro do lado do servidor
            errorMessage = `Código: ${error.status}\nMensagem: ${error.message}`;
            
            if (error.status === 401) {
                // Não autorizado - redirecionar para login
                window.location.href = '/login';
            } else if (error.status === 404) {
                errorMessage = 'Recurso não encontrado';
            } else if (error.status === 500) {
                errorMessage = 'Erro interno do servidor';
            }
        }
        
        console.error(errorMessage);
        return throwError(() => new Error(errorMessage));
    }
}

🔌 HTTP Interceptors

// interceptors/auth.interceptor.ts
import { HttpInterceptorFn, HttpRequest, HttpHandlerFn, HttpEvent } from '@angular/common/http';
import { inject } from '@angular/core';
import { Observable } from 'rxjs';

export const authInterceptor: HttpInterceptorFn = (
    req: HttpRequest<unknown>,
    next: HttpHandlerFn
): Observable<HttpEvent<unknown>> => {
    const token = localStorage.getItem('access_token');
    
    if (token) {
        const cloned = req.clone({
            headers: req.headers.set('Authorization', `Bearer ${token}`)
        });
        return next(cloned);
    }
    
    return next(req);
};

// interceptors/logging.interceptor.ts
export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
    console.log(`📡 ${req.method} ${req.url}`);
    const start = Date.now();
    
    return next(req).pipe(
        tap({
            next: () => {
                const duration = Date.now() - start;
                console.log(`✅ ${req.method} ${req.url} - ${duration}ms`);
            },
            error: (error) => {
                console.error(`❌ ${req.method} ${req.url}`, error);
            }
        })
    );
};
10

Módulo 10: RxJS e Programação Reativa

📡 RxJS 🅰️ Reativo

📡 O que é RxJS?

RxJS (Reactive Extensions for JavaScript) é uma biblioteca para programação reativa usando Observables. Angular usa RxJS extensivamente para gerenciar operações assíncronas como requisições HTTP, eventos do usuário e estados reativos.

🔍 Observable vs Promise

  • Promise: Retorna um único valor (ou erro)
  • Observable: Pode emitir múltiplos valores ao longo do tempo
  • Operadores: RxJS possui centenas de operadores para transformar dados
  • Cancelamento: Observables podem ser cancelados (unsubscribe)

🔧 Operadores RxJS Essenciais

// ========== Criação de Observables ==========
import { Observable, of, from, interval, timer, Subject, BehaviorSubject } from 'rxjs';
import { map, filter, tap, debounceTime, switchMap, catchError, take, takeUntil } from 'rxjs/operators';

// Criar Observable com valores fixos
const numeros$ = of(1, 2, 3, 4, 5);

// Criar Observable a partir de array
const usuarios$ = from(['Ana', 'Pedro', 'Maria']);

// Criar Observable com intervalo
const intervalo$ = interval(1000);  // Emite a cada 1 segundo

// Subject - Observable multicasting
const subject = new Subject<number>();
subject.subscribe(val => console.log('Sub1:', val));
subject.subscribe(val => console.log('Sub2:', val));
subject.next(1);  // Todos recebem

// BehaviorSubject - mantém valor atual
const behaviorSubject = new BehaviorSubject<string>('inicial');
behaviorSubject.subscribe(val => console.log(val));  // Recebe 'inicial'

// ========== Operadores de Transformação ==========
numeros$.pipe(
    map(x => x * 2),           // Transforma: 1,2,3,4,5 → 2,4,6,8,10
    filter(x => x > 5),        // Filtra: 6,8,10
    tap(x => console.log(x)),  // Efeito colateral (debug)
    take(2)                    // Pega apenas os 2 primeiros
).subscribe();

// ========== Exemplo Prático: Busca com debounce ==========
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { debounceTime, distinctUntilChanged, switchMap, catchError } from 'rxjs/operators';
import { of } from 'rxjs';

@Component({
    template: `
        <input [formControl]="buscaControl" placeholder="Buscar usuários...">
        <ul>
            <li *ngFor="let usuario of usuarios$ | async">{{ usuario.name }}</li>
        </ul>
    `
})
export class BuscaComponent {
    buscaControl = new FormControl('');
    usuarios$ = this.buscaControl.valueChanges.pipe(
        debounceTime(300),              // Aguarda 300ms sem digitar
        distinctUntilChanged(),         // Ignora valores repetidos
        switchMap(termo => {            // Cancela requisições anteriores
            if (termo && termo.length >= 2) {
                return this.apiService.buscarUsuarios(termo);
            }
            return of([]);
        }),
        catchError(error => {
            console.error('Erro na busca:', error);
            return of([]);
        })
    );
    
    constructor(private apiService: ApiService) { }
}

🧹 Gerenciamento de Subscriptions

// ========== Método 1: Unsubscribe manual ==========
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';

export class MeuComponente implements OnInit, OnDestroy {
    private subscriptions = new Subscription();
    
    ngOnInit() {
        const sub1 = this.apiService.getDados().subscribe();
        const sub2 = this.outroService.getDados().subscribe();
        
        this.subscriptions.add(sub1);
        this.subscriptions.add(sub2);
    }
    
    ngOnDestroy() {
        this.subscriptions.unsubscribe();  // Cancela todas subscriptions
    }
}

// ========== Método 2: Async Pipe (recomendado) ==========
// O async pipe cancela automaticamente a subscription
@Component({
    template: `
        <div *ngIf="dados$ | async as dados">
            {{ dados | json }}
        </div>
    `
})
export class MeuComponente {
    dados$ = this.apiService.getDados();
    
    constructor(private apiService: ApiService) { }
}

// ========== Método 3: takeUntil (padrão comum) ==========
import { Subject, takeUntil } from 'rxjs';

export class MeuComponente implements OnInit, OnDestroy {
    private destroy$ = new Subject<void>();
    
    ngOnInit() {
        this.apiService.getDados()
            .pipe(takeUntil(this.destroy$))
            .subscribe();
    }
    
    ngOnDestroy() {
        this.destroy$.next();
        this.destroy$.complete();
    }
}
11

Módulo 11: Pipes e Transformação de Dados

🔧 Pipes 💻 Transformação

🔧 Pipes Built-in

DatePipe

{{ data | date:'dd/MM/yyyy' }}
{{ data | date:'fullDate' }}
{{ data | date:'shortTime' }}

CurrencyPipe

{{ valor | currency:'BRL' }}
{{ valor | currency:'USD':'symbol' }}
{{ valor | currency:'EUR':'code' }}

DecimalPipe

{{ numero | number:'1.2-2' }}
{{ pi | number:'1.2-5' }}

PercentPipe

{{ 0.25 | percent }}
{{ 0.1234 | percent:'1.2-2' }}

UpperCasePipe / LowerCasePipe

{{ texto | uppercase }}
{{ texto | lowercase }}

JsonPipe

{{ objeto | json }}

🛠️ Criando Pipes Customizados

// gerar pipe: ng g pipe truncate
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
    name: 'truncate',
    standalone: true
})
export class TruncatePipe implements PipeTransform {
    
    transform(value: string, limit: number = 50, ellipsis: string = '...'): string {
        if (!value) return '';
        if (value.length <= limit) return value;
        
        return value.substring(0, limit) + ellipsis;
    }
}

// gerar pipe: ng g pipe filter
@Pipe({
    name: 'filter',
    standalone: true
})
export class FilterPipe implements PipeTransform {
    
    transform<T>(items: T[], searchTerm: string, key: keyof T): T[] {
        if (!items || !searchTerm) return items;
        
        searchTerm = searchTerm.toLowerCase();
        
        return items.filter(item => {
            const value = item[key] as string;
            return value && value.toLowerCase().includes(searchTerm);
        });
    }
}

// gerar pipe: ng g pipe safeHtml
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';

@Pipe({
    name: 'safeHtml',
    standalone: true
})
export class SafeHtmlPipe implements PipeTransform {
    
    constructor(private sanitizer: DomSanitizer) { }
    
    transform(value: string): SafeHtml {
        return this.sanitizer.bypassSecurityTrustHtml(value);
    }
}

// Uso no template
<p>{{ textoLongo | truncate:100:'...' }}</p>
<ul>
    <li *ngFor="let usuario of usuarios | filter:termo:'nome'">
        {{ usuario.nome }}
    </li>
</ul>
<div [innerHTML]="htmlContent | safeHtml"></div>
12

Módulo 12: Testes Unitários e E2E

🧪 Testes 💻 Jasmine

🧪 Testes Unitários com Jasmine e Karma

// meu-componente.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { MeuComponenteComponent } from './meu-componente.component';
import { FormsModule } from '@angular/forms';

describe('MeuComponenteComponent', () => {
    let component: MeuComponenteComponent;
    let fixture: ComponentFixture<MeuComponenteComponent>;
    
    beforeEach(async () => {
        await TestBed.configureTestingModule({
            imports: [FormsModule, MeuComponenteComponent]
        }).compileComponents();
        
        fixture = TestBed.createComponent(MeuComponenteComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });
    
    it('deve criar o componente', () => {
        expect(component).toBeTruthy();
    });
    
    it('deve ter título inicial "Meu Componente"', () => {
        expect(component.titulo).toBe('Meu Componente');
    });
    
    it('deve incrementar contador ao chamar incrementar()', () => {
        component.contador = 5;
        component.incrementar();
        expect(component.contador).toBe(6);
    });
    
    it('deve decrementar contador ao chamar decrementar()', () => {
        component.contador = 5;
        component.decrementar();
        expect(component.contador).toBe(4);
    });
    
    it('deve exibir o valor do contador no template', () => {
        component.contador = 10;
        fixture.detectChanges();
        
        const elemento = fixture.debugElement.query(By.css('.contador-valor'));
        expect(elemento.nativeElement.textContent).toContain('10');
    });
    
    it('deve emitir evento quando contador mudar', () => {
        spyOn(component.contadorAlterado, 'emit');
        
        component.incrementar();
        
        expect(component.contadorAlterado.emit).toHaveBeenCalledWith(1);
    });
});

🧪 Testes de Serviço com HttpClientTestingModule

// api.service.spec.ts
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { ApiService, Usuario } from './api.service';

describe('ApiService', () => {
    let service: ApiService;
    let httpMock: HttpTestingController;
    
    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [HttpClientTestingModule],
            providers: [ApiService]
        });
        
        service = TestBed.inject(ApiService);
        httpMock = TestBed.inject(HttpTestingController);
    });
    
    afterEach(() => {
        httpMock.verify();  // Verifica se não há requisições pendentes
    });
    
    it('deve ser criado', () => {
        expect(service).toBeTruthy();
    });
    
    it('deve buscar usuários via GET', () => {
        const usuariosMock: Usuario[] = [
            { id: 1, name: 'João', email: 'joao@email.com', username: 'joao' },
            { id: 2, name: 'Maria', email: 'maria@email.com', username: 'maria' }
        ];
        
        service.getUsuarios().subscribe(usuarios => {
            expect(usuarios).toEqual(usuariosMock);
            expect(usuarios.length).toBe(2);
        });
        
        const req = httpMock.expectOne('https://jsonplaceholder.typicode.com/users');
        expect(req.request.method).toBe('GET');
        req.flush(usuariosMock);
    });
    
    it('deve tratar erro 404', () => {
        service.getUsuario(999).subscribe({
            error: (error) => {
                expect(error.message).toContain('404');
            }
        });
        
        const req = httpMock.expectOne('https://jsonplaceholder.typicode.com/users/999');
        req.flush('Not Found', { status: 404, statusText: 'Not Found' });
    });
});
13

Módulo 13: Build e Deploy

🚀 Deploy ⚙️ Build

🚀 Build para Produção

# Build padrão
ng build

# Build otimizado para produção
ng build --prod
# ou
ng build --configuration production

# Build com análise de tamanho
ng build --stats-json

# Build com source maps (para debug)
ng build --source-map

# Build para ambiente específico
ng build --configuration staging

⚙️ Configuração de Environments

// src/environments/environment.ts (desenvolvimento)
export const environment = {
    production: false,
    apiUrl: 'http://localhost:3000/api',
    appName: 'App Dev'
};

// src/environments/environment.prod.ts (produção)
export const environment = {
    production: true,
    apiUrl: 'https://api.minhaapp.com',
    appName: 'Minha App'
};

// Uso no código
import { environment } from '../environments/environment';

export class ApiService {
    private apiUrl = environment.apiUrl;
}

🚀 Deploy em Diferentes Plataformas

📦 GitHub Pages

npm install -g angular-cli-ghpages
ng build --prod --base-href "https://usuario.github.io/repo/"
npx angular-cli-ghpages --dir=dist/nome-projeto

☁️ Vercel

npm i -g vercel
vercel
# Selecionar pasta dist/nome-projeto

🔥 Firebase Hosting

npm install -g firebase-tools
firebase login
firebase init hosting
ng build --prod
firebase deploy

🐳 Docker

# Dockerfile
FROM nginx:alpine
COPY dist/nome-projeto /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
✍️

35+ Exercícios Práticos

📝 Prática

1. Configuração de Ambiente

Instale Node.js, Angular CLI e crie seu primeiro projeto. Verifique se o servidor roda em http://localhost:4200

2. Meu Primeiro Componente

Crie um componente que exiba seu nome, idade e uma mensagem de boas-vindas usando interpolação.

3. Contador Interativo

Crie um componente com botões + e -, que atualize um contador. Adicione limite mínimo de 0 e máximo de 100.

4. Lista de Tarefas (To-Do List)

Desenvolva uma aplicação para adicionar, marcar como concluída e remover tarefas. Use *ngFor e *ngIf.

5. Formulário de Cadastro

Crie um formulário reativo com nome, email, senha e confirmação de senha com validações personalizadas.

6. Consumindo API

Consuma a API JSONPlaceholder, exiba os usuários em uma tabela e adicione um campo de busca.

7. Roteamento

Crie um site com 4 páginas (Home, Sobre, Produtos, Contato) usando Angular Router com navegação.

8. Filtro com Pipes

Crie um pipe customizado para filtrar uma lista de produtos por nome e categoria.

9. Serviço com Estado Global

Crie um serviço com BehaviorSubject para gerenciar o estado de autenticação do usuário.

10. Guarda de Rotas

Implemente um AuthGuard que protege rotas administrativas e redireciona para login se não autenticado.

11. HTTP Interceptor

Crie um interceptor que adiciona token JWT a todas as requisições e trata erros 401.

12. Upload de Arquivos

Crie um componente que permite upload de imagens com preview e validação de tamanho/tipo.

13. Formulário Dinâmico

Crie um formulário que permite adicionar/remover campos dinamicamente usando FormArray.

14. Testes Unitários

Escreva testes para um componente, um serviço e um pipe usando Jasmine.

15. Deploy (Projeto Final)

Faça o build do projeto e faça deploy no Firebase, Vercel ou GitHub Pages.

📖

Glossário Técnico

📚 Referência

🅰️ Angular CLI

Ferramenta de linha de comando para criar, gerenciar e fazer build de projetos Angular.

🧩 Componente

Bloco fundamental do Angular que controla uma parte da interface (template + lógica).

🔌 Serviço

Classe que contém lógica de negócio, compartilhada entre componentes via injeção de dependência.

📡 Observable

Objeto do RxJS que emite valores ao longo do tempo, usado para programação assíncrona.

🛣️ Router

Módulo do Angular que gerencia navegação entre componentes/páginas.

🔧 Diretiva

Classe que adiciona comportamento a elementos no DOM.

🔧 Pipe

Função que transforma dados diretamente no template.

💉 DI (Injeção de Dependência)

Padrão de design onde as dependências são fornecidas ao invés de criadas internamente.

🧪 TestBed

Classe utilitária para configurar testes unitários no Angular.

🚀 Lazy Loading

Técnica de carregar módulos sob demanda, melhorando performance inicial.

📋 Resumo do Treinamento

✅ O que você aprendeu:

  • Configurar ambiente Angular em Windows, Mac e Linux
  • Fundamentos TypeScript aplicados ao Angular
  • Criar e gerenciar componentes reutilizáveis
  • Usar diretivas estruturais e de atributo
  • Criar serviços e entender injeção de dependência
  • Implementar roteamento com guards e lazy loading
  • Construir formulários reativos e template-driven
  • Consumir APIs REST com HttpClient e interceptors
  • Programação reativa com RxJS (Observables, operadores)
  • Criar pipes customizados e usar pipes built-in
  • Escrever testes unitários com Jasmine
  • Fazer build e deploy para produção
  • 35+ exercícios práticos para fixação

📊 Seu Progresso:

Treinamento Completo ✓ | Mais de 2.500 linhas de conteúdo