Todo desenvolvedor leva um tempo até se familiarizar com um framework. Existe uma curva de aprendizado, que pode levar mais ou menos tempo dependendo da experiência de cada um. Este texto tem o propósito de mostrar o básico do Angular para ajudar aqueles que estão começando, especialmente os que já tem alguma familiaridade com o básico de frontend e/ou estão fazendo uma migração de backend para frontend. A maior parte dos exemplos utiliza recursos disponíveis nas versões mais novas, como a 18, contudo, adicionei algumas menções à versões mais antigas.
O que é o Angular?
O Angular é um framework de desenvolvimento frontend mantido pelo Google e utilizado na criação de aplicações web dinâmicas. O Angular adota uma abordagem bem completa, fornecendo um conjunto de ferramentas integradas para desenvolvimento modular, arquitetura baseada em componentes e funcionalidades nativas para gerenciamento de estados e comunicação com APIs. Na prática, você perceberá que o framework já fornece uma série de ferramentas para ajudar nas tarefas mais comuns, como formatação de valores, requisições HTTP, injetor de dependência, roteamento de páginas, interceptors, entre outros.
Componentes (components)
A unidade básica no Angular é um componente. Um componente pode ser um botão, um formulário ou uma tela em si. A dimensão do que um componente irá compor é totalmente arbitrária.
Basicamente um componente é composto de uma classe (em TypeScript) com o decorator @Component({...}) e um template (HTML). A ideia básica é que conforme alguns valores mudem dentro da classe, o HTML seja atualizado automaticamente.
Observe no exemplo abaixo onde a variável contador tem seu valor alterado conforme o usuário clica nos botões:
"Podemos dizer que o signal "empacota" um determinado valor adicionando um comportamento reativo. A ideia é notificar que houve uma alteração em um valor para quem estiver "escutando" estas mudanças, seguindo o conceito de Producer e Consumer. Desta forma um componente pode ser "avisado" que houve uma mudança e pode atualizar o DOM, sem a necessidade de percorrer toda a árvore de componentes."
Parametrizando os componentes com inputs e outputs
Além da capacidade de sincronização do HTML com os valores da classe, o componente pode receber e/ou enviar valores através dos inputs e outputs. Dessa forma, conseguimos parametrizar os componentes para aumentar seu reaproveitamento.
Observe no componente a seguir que alguns valores foram parametrizados, ou seja, serão recebidos de fora do componente. Também há um exemplo de implementação para emitir um sinal de saída, ou seja, avisar que o botão foi clicado:
Para utilizar esse componente em outras partes do código, ou seja, em outros componentes, basta importá-lo e depois utilizar o seletor <app-botoes>. Por exemplo, considere o componente abaixo que representa uma página:
Olhando o código acima, a primeira coisa que podemos notar é o uso dos colchetes [] e dos parênteses () que são usados para fazer a ligação entre o template e a lógica do componente. Os colchetes [] são usados para passar dados do componente pai para o filho, ou seja, para definir inputs para o componente. Os parênteses () são usados para registrar eventos, ou seja, para capturar eventos do componente ou de elementos HTML e chamar um método do componente pai.
Roteamento de páginas
Quando seu projeto Angular cresce e você precisa de múltiplas páginas, o componente principal (normalmente o AppComponent) se torna o container que irá hospedar as diferentes partes da sua aplicação. Dentro do AppComponent, utilizamos a diretiva <router-outlet>. Essa diretiva indica ao Angular onde ele deve renderizar o componente correspondente à rota atual da aplicação.
💡
Para quem trabalhou com Asp.Net MVC, é similar ao conceito de Master Page.
Como funciona:
As rotas são definidas em um array de objetos, onde cada objeto especifica um caminho (path) e o componente que deve ser exibido quando esse caminho for acessado.
O <router-outlet> funciona como um placeholder. Quando você navega para uma nova rota, o Angular substitui o conteúdo dentro do <router-outlet> pelo componente associado àquela rota.
Abaixo há um exemplo de código mostrando a configuração das rotas:
Para navegar entre as rotas, podemos utilizar a diretiva routerLink direto nos templates ou utilizar a classe Router:
Importante: para ambos os casos é necessário importar o RouterModule na declaração imports do componente ou módulo.
Abaixo há alguns links de posts mais antigos envolvendo o tema rotas do Angular:
Templates HTML
Assim como outros motores de renderização, como Razor (do C#), EJS (do Node.js) ou Thymeleaf (do Java), o Angular fornece uma sintaxe para controle de fluxo no template, ou seja, podemos utilizar algumas palavras reservadas para criar lógicas dentro do template, como um "if" ou um "loop for".
Serviços (services)
Responsáveis por encapsular a lógica de negócio, as classes da camada de serviço (service) podem ser reutilizadas em outras partes do projeto. Para isto, são registradas no injetor de dependência do Angular através do decorator @Injectable.
💡
O injetor de dependência do Angular permite a criação de classes que podem ser injetadas em outras partes do código, promovendo a modularidade e a testabilidade.
Para quem está familiarizado com o SpringBoot, o @Injectable do Angular funciona de forma similar ao @Service. O atributo providedIn dentro do @Injectable define o escopo de fornecimento do serviço, indicando onde o Angular deve criar e fornecer uma instância desse serviço. Por exemplo, providedIn: 'root' significa que o serviço estará disponível em toda a aplicação.
Considere o exemplo abaixo onde a classe CarrinhoService é registrada de forma global, ou seja, haverá uma única instância dessa classe para toda a aplicação. Dessa forma, podemos consumir informações dessa instância em vários componentes, criando assim um canal de comunicação entre os componentes para compartilhamento de dados, por exemplo.
Utilizando o CarrinhoService no componente ExemploServiceComponent e no CarrinhoComponent:
Módulos (NgModule)
Os módulos do Angular podem ser pensados como um agrupador. Nele, podemos declarar componentes, diretivas, pipes e serviços. Esse modelo ajuda na organização e modularização da aplicação, pois podemos organizar um conjunto de recursos dentro de módulos. Também é possível exportar recursos declarados em um módulo para serem reaproveitados em outros. Seria uma forma similar a um JAR para quem programa em Java ou DLL para quem trabalha com C#.
Em versões anteriores ao Angular 14, essa organização ao redor dos módulos era obrigatória, e cada componente precisava estar associado a um módulo. A partir do Angular 14, foi introduzido a opção standalone, que permite que os componentes, pipes e diretivas não necessitem ser declarados em um módulo.
Um exemplo bem comum sobre o uso de módulos no Angular, é o uso de um módulo compartilhado, normalmente chamado de SharedModule. O objetivo é declarar (declarations) e exportar (exports) componentes comuns a todo projeto. Desta forma basta importar este módulo para ter acesso a todos os recursos exportados. Exemplo retirado da documentação oficial do Angular:
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CustomerComponent } from './customer.component';
import { NewItemDirective } from './new-item.directive';
import { OrdersPipe } from './orders.pipe';
@NgModule({
imports: [CommonModule],
declarations: [
CustomerComponent,
NewItemDirective,
OrdersPipe
],
exports: [
CustomerComponent,
NewItemDirective,
OrdersPipe,
CommonModule,
FormsModule
],
})
export class SharedModule { }
Diretivas (directives)
As diretivas são como atributos de um elemento HTML que o Angular identifica e aplica uma determinada lógica. Por exemplo, considere a diretiva abaixo que quando usada, modifica o visual do elemento HTML nos eventos mouseover e mouseout:
Diretivas são como "receitas" que você dá ao Angular para que ele modifique o comportamento ou a aparência de um elemento. Elas são como pequenas instruções que você insere no seu HTML para dizer ao Angular o que fazer com aquele elemento.
💡
Existem várias diretivas prontas para uso. Vale dar uma olhada na documentação oficial para conhecer melhor: https://angular.dev/guide/directives
Pipes
Os pipes (|) ajudam na formatação e transformação de dados no Angular. Com ele é possível separar responsabilidades. Por exemplo, considere o código abaixo que alimenta a variável data e depois aplica uma formatação no dataFormatada:
💡
Podemos declarar o conteúdo do template de um componente inline, ou seja, direto no atributo template, conforme abaixo. A alternativa é utilizar um arquivo externo, neste caso basta indicar o nome do arquivo no atributo templateUrl.
@Compoment({
standalone: true,
imports: [CommonModule],
template: `Data: {{ dataFormatada }}`,
})
export class TestComponent {
dataFormatada: string = '';
data: Date = new Date();
obterValores() {
this.data = new Date('2024-01-01T12:00:00.000Z');
const day = String(data.getDate()).padStart(2, '0');
const month = String(data.getMonth() + 1).padStart(2, '0');
const year = data.getFullYear();
this.dataFormatada = `${day}/${month}/${year}`;
}
}
Utilizando um pipe é possível simplificar o código acima, além de separar as responsabilidades de formatação de dados:
@Compoment({
standalone: true,
imports: [CommonModule],
template: `Data: {{ data | date : 'dd/MM/yyyy }}`,
})
export class TestComponent {
data: Date = new Date();
obterValores() {
this.data = new Date('2024-01-01T12:00:00.000Z');
}
}
Via de regra, sempre que precisar formatar ou transformar um dado para exibição, sempre opte por um pipe.
Abaixo deixo algumas referências de textos publicados por aqui envolvendo o Angular Pipe:
Considerações
O Angular vai muito além do que foi comentado neste texto. Contudo, o objetivo do texto é mostrar o básico do framework de uma forma bem rápida e prática. Abaixo há uma lista dos itens abordados de uma forma mais resumida:
Componentes: São a base da construção de interfaces no Angular, encapsulando tanto a lógica quanto a visualização. São marcados com @Component() e podem representar desde páginas inteiras até elementos simples como botões.
Injectables: Registram classes no injetor de dependências do Angular, permitindo que sejam injetadas em outros componentes, diretivas e serviços. Similar ao @Service do Spring Boot.
Diretivas: Adicionam comportamentos e modificações a elementos HTML. Existem diretivas estruturais (como *ngIf, *ngFor) que alteram o DOM e diretivas de atributos (comongClass, ngStyle) que modificam propriedades de elementos.
Pipes: Transformam dados para exibição. Exemplo: formatar datas, converter maiúsculas para minúsculas, etc.
Módulos: Agrupam componentes, diretivas, pipes e serviços, definindo o contexto para injeção de dependências. Podemos fazer uma analogia a um namespace do C# ou package do Java, onde agrupamos um determinado conjunto de recurso que podem ser expostos ao mundo externo de acordo com as configurações desejadas.
Neste link há uma sugestão de roadmap para aprendizado, talvez possa ajudar a organizar seus estudos.
No link abaixo, deixo a listagem de todo o conteúdo publicado por aqui a respeito do Angular: