📚 Módulo 06: Frontend - Setup Angular Standalone e Rotas
✅ Pré-Requisitos deste Módulo
Confirme antes de começar:
node --version # deve retornar v18.x
ng version # deve retornar Angular CLI: 18.x
O backend do Módulo 05 deve estar rodando (./mvnw spring-boot:run) para que os testes de integração funcionem nos próximos módulos. Consulte o Guia de Setup se algum dos comandos falhar.
Neste módulo, mudamos de ambiente e iniciamos o desenvolvimento do nosso cliente SPA (Single Page Application) utilizando o Angular 18+.
Aprenderemos a configurar a infraestrutura do Angular baseada no padrão moderno Standalone Components (que elimina o acoplamento do antigo NgModule), estruturaremos nossos modelos de dados em interfaces TypeScript e criaremos o roteador dinâmico de navegação.
🏗️ 1. O que são Standalone Components?
Até o Angular 14, toda a estruturação de dependências dependia dos arquivos @NgModule. A partir das versões estáveis recentes, o Angular adota a arquitetura de Componentes Independentes (Standalone) por padrão.
🧠 Principais Benefícios:
- Redução de Boilerplate: Menos arquivos burocráticos para configurar. Cada componente declara suas próprias importações diretamente.
- Melhor Desempenho (Tree Shaking): O compilador do Angular remove códigos e bibliotecas importadas que não estão sendo efetivamente usadas, reduzindo o tamanho final do arquivo distribuído ao navegador.
- Roteamento Facilitado: O roteador pode carregar componentes sob demanda (Lazy Loading) de forma nativa e transparente.
📋 Pré-requisitos
Antes de inicializar o projeto Angular, certifique-se de que você tem o Node.js (versão 18 ou superior) instalado em seu computador. Em seguida, instale o Angular CLI globalmente para ter acesso aos utilitários de geração de código executando o comando abaixo no seu terminal:
# Instalar o Angular CLI globalmente
npm install -g @angular/cli
# Verificar se a instalação ocorreu com sucesso
ng version
Para criar o projeto frontend, execute em seu terminal:
ng new web --routing=true --style=css --ssr=false
(Nota: Escolhemos desativar o Server-Side Rendering (SSR) com --ssr=false para manter a didática focada em uma SPA clássica desacoplada).
Após criar o projeto, edite src/index.html para incluir as fontes e a biblioteca de ícones Bootstrap Icons via CDN:
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<title>TecLoja — Tecnologia & Inovação</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Fontes Premium do Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<!-- Biblioteca Bootstrap Icons para Ícones -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
</head>
<body>
<app-root></app-root>
</body>
</html>
📝 2. Modelagem de Dados em TypeScript
Para garantir a tipagem estática e evitar erros de consistência de dados ao nos comunicarmos com o backend, criaremos as interfaces que espelham exatamente os contratos DTO da nossa API.
Crie os arquivos correspondentes na pasta src/app/models/:
1. auth.model.ts
export interface LoginResponse {
username: string;
role: string;
token: string;
}
2. categoria.model.ts
export interface Categoria {
id: number;
nome: string;
}
3. produto.model.ts
export interface Produto {
id?: number;
nome: string;
descricao: string;
preco: number;
estoque: number;
ativo?: boolean;
categoriaId: number;
categoriaNome?: string;
}
4. item-pedido.model.ts
export interface ItemPedidoForm {
produtoId: number;
quantidade: number;
}
export interface ItemPedido {
id: number;
produtoId: number;
produtoNome: string;
quantidade: number;
precoUnitario: number;
subtotal: number;
}
5. pedido.model.ts
import { ItemPedido, ItemPedidoForm } from './item-pedido.model';
export interface PedidoForm {
clienteId: number;
itens: ItemPedidoForm[];
}
export interface Pedido {
id: number;
dataPedido: string;
status: string;
clienteId: number;
clienteNome: string;
itens: ItemPedido[];
valorTotal: number;
}
🗺️ 3. Roteamento Dinâmico (src/app/app.routes.ts)
O roteador do Angular gerencia qual componente visual será exibido com base no endereço da URL no navegador.
Mapearemos rotas públicas para a loja (catálogo e carrinho) e rotas protegidas (restritas à administração dos eletrônicos). Note o carregamento sob demanda via loadComponent para melhor performance.
flowchart TD
A[AppComponent] --> B((RouterOutlet))
B -->|/| C[CatalogoComponent]
B -->|/carrinho| D[CarrinhoComponent]
B -->|/login| E[LoginComponent]
B -->|/admin/...| F[Rotas Lazy-Loaded Protegidas]
F -.-> G[AdminProdutosComponent]
F -.-> H[ProdutoFormComponent]
style F stroke-dasharray: 5 5
import { Routes } from '@angular/router';
import { adminGuard } from './guards/admin.guard';
export const routes: Routes = [
// Rotas Públicas da Loja
{
path: '',
loadComponent: () => import('./components/catalogo/catalogo.component').then(m => m.CatalogoComponent)
},
{
path: 'carrinho',
loadComponent: () => import('./components/carrinho/carrinho.component').then(m => m.CarrinhoComponent)
},
{
path: 'login',
loadComponent: () => import('./components/login/login.component').then(m => m.LoginComponent)
},
// Rotas Administrativas Privadas (Protegidas por Guarda de Rota)
{
path: 'admin/produtos',
loadComponent: () => import('./components/admin-produtos/admin-produtos.component').then(m => m.AdminProdutosComponent),
canActivate: [adminGuard]
},
{
path: 'admin/produtos/novo',
loadComponent: () => import('./components/produto-form/produto-form.component').then(m => m.ProdutoFormComponent),
canActivate: [adminGuard]
},
{
path: 'admin/produtos/editar/:id',
loadComponent: () => import('./components/produto-form/produto-form.component').then(m => m.ProdutoFormComponent),
canActivate: [adminGuard]
},
// Rota Curinga para redirecionar endereços inválidos
{
path: '**',
redirectTo: ''
}
];
⚙️ 4. Configuração Standalone Global (src/app/app.config.ts)
Como não temos o NgModule, a inicialização de serviços globais (como o roteador e o cliente de requisições HTTP) é centralizada no arquivo de configuração do bootstrap do Angular.
Note a injeção funcional do interceptor de autenticação JWT, que aprenderemos a implementar no próximo módulo.
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { routes } from './app.routes';
import { authInterceptor } from './interceptors/auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
// Otimização de detecção de mudanças por zonas
provideZoneChangeDetection({ eventCoalescing: true }),
// Roteamento global (com amarração automática de parâmetros de URL para Inputs)
provideRouter(routes, withComponentInputBinding()),
// Injeção do HttpClient de requisições com o interceptor de segurança anexado
provideHttpClient(
withInterceptors([authInterceptor])
)
]
};
🖥️ 5. O Componente Raiz (src/app/app.component.ts)
O componente raiz atua como a “casca” principal da aplicação. Ele simplesmente consome o roteador e renderiza as telas dinâmicas por meio da tag <router-outlet>.
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
template: `
<router-outlet></router-outlet>
`
})
export class AppComponent {}
🤔 Por que --ssr=false ao criar o projeto?
SSR (Server-Side Rendering) faz o Angular renderizar o HTML no servidor antes de enviar ao navegador — útil para SEO em portais públicos. Para esta disciplina, queremos uma SPA clássica 100% no cliente, sem um servidor de renderização extra para gerenciar. O --ssr=false elimina essa camada de complexidade e mantém o foco no aprendizado de componentes e roteamento.
🤔 Por que loadComponent em vez de importar o componente direto na rota?
loadComponent usa Lazy Loading: o JavaScript do componente só é baixado pelo navegador quando o usuário navega para aquela rota pela primeira vez. Com importação direta, todo o código seria carregado ao abrir a página inicial, aumentando o tempo de primeiro carregamento. Em apps reais com dezenas de telas, a diferença é significativa.
🔍 Checkpoint
Após criar o projeto e os arquivos de rotas e configuração:
cd web
npm start
Abra http://localhost:4200 no navegador.
Resultado esperado: A página abre sem erros no console. Ao navegar para http://localhost:4200/login, o Angular não exibe erro 404 (porque a rota curinga ** redireciona para /). As rotas só exibirão conteúdo real após criar os componentes nos próximos módulos.
⚠️ Erros Comuns
| Sintoma | Causa | Solução |
|---|---|---|
Could not find module @angular/core |
Dependências não instaladas | Execute npm install dentro da pasta web/ |
| Página em branco sem erros | app.component.ts não tem RouterOutlet |
Confirme imports: [RouterOutlet] no AppComponent |
Cannot find module './interceptors/auth.interceptor' |
Arquivo do interceptor não criado | Crie src/app/interceptors/auth.interceptor.ts (Módulo 07) |
NG8001: 'router-outlet' is not a known element |
RouterOutlet não importado |
Adicione RouterOutlet nos imports do AppComponent |
Servidor não inicia após ng new |
Angular SSR instalado por padrão | Use a flag --ssr=false ao criar, ou remova a dependência @angular/ssr do package.json |
🏁 Conclusão e Próximos Passos
A base do nosso frontend em Angular está perfeitamente pavimentada! Você configurou o projeto Standalone, mapeou as tipagens necessárias e estabeleceu as rotas de navegação da TecLoja.
No Módulo 07, programaremos a segurança e a infraestrutura de comunicação do frontend: construindo o serviço de autenticação reativo baseado no inovador sistema de Angular Signals, estruturando o interceptor HTTP para anexar tokens Bearer automaticamente e modelando a interface com CSS nativo e moderno.