📚 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:

📋 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.


Voltar para o Sumário