HttpClient e Envio de Requisições em Angular
Para interagir com o servidor e enviar requisições HTTP, o Angular utiliza a classe HttpClient
. Essa classe define uma série de métodos para enviar diferentes tipos de requisições, como GET
, POST
, PUT
e DELETE
. A HttpClient
é construída sobre o objeto XMLHttpRequest
do JavaScript.
Para usar essa classe no projeto, é necessário importar provideHttpClient
de @angular/common/http
no arquivo main.ts
:
import { bootstrapApplication } from "@angular/platform-browser";
import { AppComponent } from "./app/app.component";
import { provideHttpClient } from "@angular/common/http";
bootstrapApplication(AppComponent, { providers: [provideHttpClient()] }).catch(
(err) => console.error(err)
);
Vamos começar com a execução de uma requisição GET
simples. Suponha que temos a estrutura padrão de um projeto Angular:
helloapp/ ├── src/ │ ├── app/ │ │ ├── app.component.ts │ ├── main.ts │ ├── index.html ├── angular.json ├── package-lock.json ├── package.json └── tsconfig.json
Agora, faremos uma requisição a um arquivo JSON que conterá alguns dados. A pasta principal dos arquivos da aplicação é a src
. Nela, criaremos uma nova pasta chamada assets
e, em seguida, adicionaremos o arquivo data.json
com os seguintes dados:
{
"name": "Bob",
"age": 28
}
Para representar esses dados, adicione um novo arquivo user.ts
na pasta src/app com o seguinte código:
export class User {
constructor(public name: string, public age: number) {}
}
A estrutura do projeto agora é a seguinte:
helloapp/ ├── src/ │ ├── app/ │ │ ├── app.component.ts │ │ ├── user.ts │ ├── assets/ │ │ ├── data.json │ ├── main.ts │ ├── index.html ├── angular.json ├── package-lock.json ├── package.json └── tsconfig.json
Agora, vamos definir o código no componente AppComponent
para enviar a requisição:
import { Component, OnInit } from "@angular/core";
import { HttpClient, HttpClientModule } from "@angular/common/http";
import { User } from "./user";
@Component({
selector: "my-app",
standalone: true,
imports: [HttpClientModule],
template: `<div>
<p>Nome do usuário: {{ user?.name }}</p>
<p>Idade do usuário: {{ user?.age }}</p>
</div>`,
})
export class AppComponent implements OnInit {
user: User | undefined;
constructor(private http: HttpClient) {}
ngOnInit() {
this.http.get("assets/data.json").subscribe({
next: (data: any) => (this.user = new User(data.name, data.age)),
});
}
}
Neste exemplo, os dados do objeto User
são exibidos no template, obtidos através da requisição ao servidor. No entanto, carregar dados diretamente no construtor do componente não é ideal. O método ngOnInit()
, que faz parte da interface OnInit
e é chamado na inicialização do componente, é um local mais apropriado para carregar dados. No construtor, apenas recebemos o serviço HttpClient
.
No método ngOnInit()
, fazemos a requisição usando o serviço HttpClient
. O método http.get()
retorna um objeto Observable<Object>
. O Observable
funciona como um fluxo de dados, e para ouvir os eventos desse fluxo, utilizamos o método subscribe
. Esse método define a ação a ser executada quando os dados são recebidos do servidor. No caso, usamos o parâmetro next
para definir a ação, representada por uma função de seta:
(data: any) => (this.user = new User(data.name, data.age));
Como o arquivo JSON contém dados diretamente relacionados ao modelo da classe User
, os dados recebidos podem ser passados diretamente para o construtor da classe.
Já que o arquivo JSON é um arquivo auxiliar, precisamos informá-lo ao Angular CLI no arquivo angular.json
, usando o parâmetro "assets": ["src/assets"]
:
{
"version": 1,
"projects": {
"helloapp": {
"projectType": "application",
"root": "",
"sourceRoot": "src",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/helloapp",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.json",
"assets": ["src/assets"],
"aot": true
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"buildTarget": "helloapp:build"
}
}
}
}
}
}
Ao rodar o projeto, os dados carregados do arquivo data.json
serão exibidos corretamente na página da web.
Criando um Serviço
Ao interagir com o servidor, é comum que as requisições sejam feitas através de serviços auxiliares em vez de diretamente no componente. Isso ocorre porque um serviço pode encapsular a lógica de processamento dos dados recebidos, evitando sobrecarregar o código do componente. Além disso, os serviços podem fornecer funcionalidades que serão usadas por vários componentes. Assim, os componentes atuam como consumidores dos dados fornecidos pelos serviços.
Para trabalhar com requisições HTTP, adicione um novo arquivo http.service.ts
na pasta src/app
com o seguinte conteúdo:
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
@Injectable()
export class HttpService {
constructor(private http: HttpClient) {}
getData() {
return this.http.get("assets/data.json");
}
}
Nesse caso, o serviço recebe um objeto HttpClient
para enviar requisições. A classe é decorada com @Injectable
, garantindo que o mecanismo de injeção de dependências do Angular possa criar uma instância desse serviço e injetá-la em outros objetos (como componentes ou outros serviços).
Para realizar uma requisição GET
, o método get()
é chamado no objeto HttpClient
, passando o endereço da requisição: neste caso, um arquivo JSON contendo os dados.
Agora, vamos utilizar o serviço no componente AppComponent
:
import { Component, OnInit } from "@angular/core";
import { HttpService } from "./http.service";
import { User } from "./user";
@Component({
selector: "my-app",
standalone: true,
imports: [],
template: `<div>
<p>Nome do usuário: {{ user.name }}</p>
<p>Idade do usuário: {{ user.age }}</p>
</div>`,
providers: [HttpService],
})
export class AppComponent implements OnInit {
user: User;
constructor(private httpService: HttpService) {}
ngOnInit() {
this.httpService.getData().subscribe({
next: (data: any) => (this.user = new User(data.name, data.age)),
});
}
}
O serviço HttpService
é injetado no construtor do componente AppComponent
. O método getData()
é chamado no serviço para obter os dados. O método subscribe()
é usado para ouvir os eventos do fluxo de dados e definir a ação a ser executada quando os dados são recebidos.
Carregando Dados Complexos
Nos exemplos anteriores, os dados no arquivo JSON correspondiam diretamente à definição da classe User
. Assim, podíamos simplesmente atribuir this.user = new User(data.name, data.age)
sem problemas. No entanto, também é possível carregar dados mais complexos. Vamos modificar o arquivo data.json
para incluir uma lista de usuários:
{
"userList": [
{ "name": "Bob", "age": 28 },
{ "name": "Tom", "age": 45 },
{ "name": "Alice", "age": 32 }
]
}
O código do http.service.ts
continua o mesmo.
Agora, vamos modificar o componente para lidar com uma lista de usuários:
import { Component, OnInit } from "@angular/core";
import { HttpService } from "./http.service";
import { User } from "./user";
@Component({
selector: "my-app",
standalone: true,
imports: [],
template: `<ul>
@for(user of users; track $index){
<li>
<p>Nome do usuário: {{ user?.name }}</p>
<p>Idade do usuário: {{ user?.age }}</p>
</li>
}
</ul>`,
providers: [HttpService],
})
export class AppComponent implements OnInit {
users: User[] = [];
constructor(private httpService: HttpService) {}
ngOnInit() {
this.httpService.getData().subscribe({
next: (data: any) => (this.users = data["userList"]),
});
}
}
Neste exemplo, queremos obter um array de objetos User
. Entretanto, os dados recebidos do arquivo data.json
não são diretamente um array, mas sim um objeto com uma chave "userList"
, que contém o array de usuários. Usamos essa chave para acessar os dados corretos e os atribuímos à propriedade users
do componente:
this.users = data["userList"];
Agora, o componente exibirá a lista de usuários carregados do arquivo data.json
corretamente.