Requisições HTTP em Angular
No Angular, as requisições HTTP são fundamentais para a comunicação com serviços externos, como APIs RESTful, para obter e enviar dados. O Angular fornece o módulo HttpClient para fazer requisições HTTP de forma eficiente e fácil de usar.
Usando o HttpClient
Para o Angular versão 17, onde os módulos não são mais o padrão, antes de usar o HttpClient em seus serviços e componentes é necessário declara-lo como um provider no app.config:
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [provideHttpClient()],
};
Em um módulo Angular, para utilizar o HttpClient, você precisa importar o HttpClientModule no módulo, geralmente no módulo principal ou em módulos específicos onde você planeja fazer as requisições.
O HttpClient que vem do @angular/common/http é usado ao injetar sua dependência nos serviços ou componentes onde você deseja fazer requisições HTTP, usando o construtor da classe ou usando a função inject que o Angular fornece:
constructor(private httpClient: HttpClient) {}
//Ou
httpClient = inject(HttpClient)
Com o HttpClient, é possível acessar os métodos GET, POST, PUT, DELETE, entre outros. Independente do método, o primeiro parâmetro é o endereço http da requisição, se o método for de insert ou update como o POST, PUT, o segundo parâmetro é o body da requisição, e por fim, vem um objeto options opcional que permite incluir Header, Params entre outros.
// Fazendo uma requisição GET
this.http.get('https://api.example.com/data');
// Fazendo uma requisição GET com Header
this.http.get('https://api.example.com/data', {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
});
// Fazendo uma requisição POST
this.http.post('https://api.example.com/post-data', { body: 'data' });
Observers
O módulo http do Angular trabalha com Observables, que fazem parte da programação reativa do RxJs. Um Observable é uma abstração que representa uma sequência de valores ou eventos que ocorrem ao longo do tempo. Um Observable possui dois métodos o subscribe e o pipe. o pipe vai servir como um filtro para passar os dados antes de usar o subscribe que irá fazer uma inscrição para aquele Observable. O subscribe é acionado quando a requisição finaliza seja com sucesso ou não e retorna a resposta da requisição, aqui, podemos passar o valor da requisição para uma variável ou fazer qualquer outra coisa necessário quando a requisição finaliza, basicamente seria o then de uma Promise. Existe muito mais coisa envolta de um Observable, é um conteúdo que vale a pena estudar.
Exemplo de Uso
Para seguir os conceitos de arquitetura limpa, requisições http são feitas em services. Dessa forma, vamos criar um serviço de possuirá um CRUD de heróis, um herói terá um nome de um alter-ego:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface Hero {
id?: number;
name: string;
alterEgo: string;
}
@Injectable({
providedIn: 'root'
})
export class HeroService {
private apiUrl = 'https://api.example.com/heroes'; // URL da API
constructor(private http: HttpClient) {}
// Listar todos os heróis
getHeroes(): Observable<Hero[]> {
return this.http.get<Hero[]>(this.apiUrl);
}
// Adicionar um novo herói
addHero(hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.apiUrl, hero);
}
// Atualizar um herói existente
updateHero(hero: Hero): Observable<Hero> {
const url = `${this.apiUrl}/${hero.id}`;
return this.http.put<Hero>(url, hero);
}
// Excluir um herói existente
deleteHero(id: number): Observable<void> {
const url = `${this.apiUrl}/${id}`;
return this.http.delete<void>(url);
}
}
Para usar esse serviço em um componente, podemos fazer dessa forma:
import { Component, OnInit } from '@angular/core';
import { Hero, HeroService } from './hero.service';
@Component({
selector: 'app-hero-list',
standalone: true,
template: `
<!-- Exemplo de uso em um template -->
<ul>
<li *ngFor="let hero of heroes">
{{ hero.name }} ({{ hero.alterEgo }})
<button (click)="deleteHero(hero.id)">Excluir</button>
</li>
</ul>
`
})
export class HeroListComponent implements OnInit {
heroes: Hero[] = [];
constructor(private heroService: HeroService) {}
ngOnInit() {
this.getHeroes();
}
getHeroes() {
this.heroService.getHeroes().subscribe(heroes => {
this.heroes = heroes;
});
}
deleteHero(id: number) {
this.heroService.deleteHero(id).subscribe(() => {
this.getHeroes(); // Atualiza a lista após a exclusão
});
}
}
Usamos o ngOnInit que será acionado quando o componente rendenizar, para popular o array de heróis, os métodos que chamam o serviço, após fazer a requisição faz um subcribe para atualizar a lista de heróis, chamando novamente getHeroes.
Uma outra abordagem
No Angular existe um pipe chamado async ele faz um subscribe em um Observable automaticamente, removendo a necessidade de sempre que fazer uma requisição, fazer o subscribe para atualizar os dados.
No componente HeroListComponent no lugar de termos um:
heroes: Hero[] = [];
teríamos:
heroes$: Observable<Hero[]> = [];
Ou seja, no lugar de um array de heróis teríamos um Obervablede um array de heróis. um $ usado no nome da variável é apenas uma convenção usada, que significa que aquela variável se trata de um Obseravble. A função getHeroes ficaria assim:
getHeroes() {
this.heroes$ = this.heroService.getHeroes();
}.
e para usa-la no código, basta acompanha-lo no pipe async.
<ul>
<li *ngFor="let hero of heroes$ | async">
{{ hero.name }} ({{ hero.alterEgo }})
<button (click)="deleteHero(hero.id)">Excluir</button>
</li>
</ul>
Vantagens ao usar essa abordagem:
- Sintaxe Mais Limpa:
- O uso do
asyncpipe diretamente no template elimina a necessidade de fazer umsubscribemanual e gerenciar a assinatura no código do componente. Isso resulta em um código mais limpo e mais fácil de entender.
- O uso do
- Tratamento Automático de Mudanças:
- O
asyncpipe lida automaticamente com mudanças na resposta assíncrona. Se a resposta da solicitação HTTP for atualizada, oasyncpipe cuidará automaticamente da atualização no template.
- O
- Evita Memory Leaks:
- O
asyncpipe lida automaticamente com a desinscrição (unsubscribe) quando o componente é destruído. Isso ajuda a evitar potenciais memory leaks que podem ocorrer se você esquecer de desinscrever manualmente.
- O
- Evita Problemas de Zone.js:
- O
asyncpipe está integrado ao sistema de detecção de mudanças do Angular e funciona bem com a Zone.js. Isso pode ajudar a evitar problemas com a detecção de mudanças quando a resposta assíncrona é atualizada.
- O