HTTP
Um recurso muito útil no desenvolvimento de software web é consultar fontes de dados. Por exemplo, suponha que um conjunto de objetos esteja presente em um arquivo ou então esteja disponível por meio de uma URL de uma API. Para usar esse rcurso, o Angular fornece o HttpModule
.
O HttpModule
é disponibilizado pelo pacote @angular/http
e é preciso registrá-lo no AppModule
:
import { HttpModule } from '@angular/http';
@NgModule({
imports : [ BrowserModule, FormsModule, HttpModule ],
...
})
export class AppModule { }
Consultando dados de um arquivo .json
Uma fonte de dados pode ser um arquivo .json. Por exemplo (arquivo eventos.json
):
[
{
"id": 1,
"nome": "XIX Congresso de Computação e Sistemas de Informação",
"sigla": "ENCOINFO"
},
{
"id": 2,
"nome": "XIII Simpósio Brasileiro de Sistemas de Informação",
"sigla": "SBSI"
},
{
"id": 3,
"nome": "XXXVII Congresso da Sociedade Brasileira de Computação",
"sigla": "CSBC"
}
]
Para consultar o arquivo podemos usar o serviço Http
, disponibilizado pelo módulo HttpModule
. Para isso, é necessário, primeiro, injetá-lo no construtor de um serviço:
import { Injectable } from '@angular/core';
import { Evento } from './Evento';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
@Injectable()
export class EventosService {
constructor(private http : Http) {
}
...
}
Depois, o serviço pode disponibilizar métodos que consultem e retornem o conteúdo do arquivo. Por exemplo, o método all()
:
all() : Observable<Evento[]>{
return this.http.get('../../public/dados/eventos.json')
.map(response => response.json() as Evento[]);
}
O serviço Http
fornece o método get()
, que aceita um caminho (ou uma URL) para o arquivo .json (neste caso eventos.json
). O retorno desse método é do tipo Observable
sobre o qual chamamos a função map()
, que faz algum processamento sobre o retorno do método anterior e o retorna.
A sintaxe response => response.json()
é chamada de lambda ou função seta (arrow function) e permite passar uma função como callback para o método map()
. Neste caso, a função lambda tem um parâmetro, chamado response
, e o retorno da função é response.json()
. Por fim, este valor é convertido para Evento[]
utilizando o operador as
.
Para usar o serviço, o componente deve interagir com ele conforme um ciclo de vida específico (e assíncrono) (trecho de código a seguir é do componente EventoManagerComponent
):
ngOnInit(): void {
this.eventosService.all().subscribe(eventos => this.eventos = eventos);
}
Para utilizar o valor de um Observable
é necessário chamar o método subscribe()
, que aceita três parâmetros:
- a callback que trata do retorno da
Observable
que opera com sucesso - a callback que trata do retorno da
Observable
quando ocorre um erro - a callback que executa quando o processo é concluído
Lidar com esse ciclo de vida é importante porque seu comportamento assíncrono muda a forma de utilizar os métodos -- por exemplo, não há um retorno do método para ser usado diretamente.
Mais sobre serviços
Esta seção demonstra como criar um serviço para usar o serviço Http
e consumir uma API.
Exemplo (trecho do arquivo src\/app\/eventos.service.ts):
import { Injectable } from '@angular/core';
import { Http, Response, Headers, RequestOptions } from '@angular/http';
import { Evento } from './evento';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class EventosService {
constructor(private http: Http) { }
getEventos(filtro: string) : Observable<Evento[]> { }
private extractData(res: Response) { }
private handleError(error: any) { }
save(evento: Evento) : Observable<Evento> { }
delete(evento: Evento) : Observable<number> { }
}
Note a presença da função @Injectable()
(do pacote @angular/core
). Essa anotação da classe é importante para informar a outras classes que EventosService
deve ser utilizada como um serviço.
No construtor é utilizado o recurso de injeção de dependência para que a classe EventosService
possa usar a classe Http
(fornecida pelo pacote @angular/http
), que fornece acesso a recursos de comunicação sobre HTTP utilizando XHR (XmlHttpRequest).
Considera-se neste capítulo que a API utilizada tem a seguinte estrutura:
As rotas são:
GET /eventos
: retorna um objeto{data:[]}
representando os eventos existentes. Essa rota aceita o parâmetro de URLq
, que contém uma string usada para filtrar o conjunto de dadosGET /eventos/{id}
: retorna um objeto{data: {}}
representando um evento cujo identificador é representado pelo parâmetro de rotaid
POST /eventos/{id}
: salva (cadastra ou atualiza) os dados de um evento cujo identificador é representado pelo parâmetro de rotaid
e os dados estão no corpo do protocolo HTTP. O retorno é um objeto{data:{}}
com os dados atualizados do evento salvoDELETE /eventos/{id}
: exclui um evento cujo identificador é representado pelo parâmetro de rotaid
. O retorno é um objeto{data:number}
(com1
indicando que o evento foi excluído e0
, que não foi incluído)
Consultando eventos
O método getEventos(filtro: string): Observable<Evento[]>
utiliza o conceito de "Observable" para consultar dados via HTTP. Pela definição da API, utiliza a rota GET /eventos
:
getEventos(filtro: string) : Observable<Evento[]> {
return this.http.get(this.apiUrl + '?q=' + filtro)
.map(this.extractData)
.catch(this.handleError);
}
Salvando eventos
O método save(evento: Evento) : Observable<Evento>
utiliza http.post()
e envia os dados de um evento para a rota POST /eventos/{id}
da API:
save(evento: Evento) : Observable<Evento> {
let body = JSON.stringify(evento);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post(this.apiUrl + evento.id, body, options)
.map(this.extractData)
.catch(this.handleError);
}
Requisições POST precisam de mais informações do que requisições GET. Por isso, o método http.post()
aceita mais parâmetros:
this.apiUrl + evento.id
: representa a URL da requisiçãobody
: representa os dados que estão sendo enviados. O objetobody
é uma string criada a partir da chamada deJSON.stringify()
recebendo como parâmetro o objetoevento
(o objeto que tem os dados que devem ser salvos), que é convertido para stringoptions
: é um objeto do tipoRequestOptions
que é usado para indicar que a requisição utiliza o tipoapplication/json
.
Excluindo eventos
O método delete(evento: Evento) : Observable<number>
utiliza http.delete()
e envia o identificador de um evento para a rota DELETE /eventos/{id}
da API:
delete(evento: Evento) : Observable<number> {
return this.http.delete(this.apiUrl + evento.id)
.map(this.extractData)
.catch(this.handleError);
}
A estrutura de http.delete()
é bastante similar à de http.get()
.