saber_de_detalhes_pode_ser_ruim - VWJavascript/Alurapic GitHub Wiki
Aos poucos nossa aplicação vai ganhando forma, mas ainda há algo que podemos melhorar internamente. Veja que nos componentes ListagemComponent e CadastroComponent usamos o serviço Http para consumirmos dados do nosso servidor. No Cadastro,foi necessário configurar o header da requisição e tanto nele quando em ListagemComponent, declaramos o endereço v1/fotos. E se o endereço do recurso mudar? Precisaremos lembrar de alterar nesses dois lugares.
Podemos isolar todo o código que interage com o servidor em uma classe. Todos os componentes que quiserem se integrar com o servidor terão como dependência essa classe. Como centralizamos toda a lógica de acesso em um lugar apenas, qualquer modificação será propagada em todos que fizerem uso da nossa classe.
Vamos criar o arquivo alurapic/client/app/foto/foto.service.ts. O construtor do nosso serviço terá como dependência Http, que será injetado pelo Angular. Inclusive, no próprio construtor já vamos configurar uma instância de Headers para que possamos utilizá-la com Http nos métodos lista e cadastra:
// alurapic/client/app/foto/foto.service.ts
import { Http, Headers } from '@angular/http';
import { FotoComponent } from './foto.component';
export class FotoService {
http: Http;
headers: Headers;
url: string = 'v1/fotos';
constructor(http: Http) {
this.http = http;
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
}
lista() {}
cadastra(foto: FotoComponent) {}
}
Veja que temos uma propriedade que guarda a URL para não termos que repeti-la nos métodos lista e cadastra. Aliás, Repare que o método cadastra recebe como parâmetro o tipo FotoComponent. Mais uma vez estamos utilizando a tipamente estática do TypeScript para garantir que apenas instâncias desse componente podem ser passadas como parâmetro.
Agora vamos implementar os métodos lista e cadastra:
// alurapic/client/app/foto/foto.service.ts
import { Http, Headers } from '@angular/http';
import { FotoComponent } from './foto.component';
export class FotoService {
http: Http;
headers: Headers;
url: string = 'v1/fotos';
constructor(http: Http) {
this.http = http;
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
}
lista() {
return this.http.get(this.url)
.map(res => res.json());
}
cadastra(foto: FotoComponent) {
return this.http.post(this.url, JSON.stringify(foto),
{ headers: this.headers });
}
}
Veja que as implementações são idênticas às que fizemos nos componentes ListagemComponent e CadastroComponent. No entanto, já aprendemos a tipar o retorno de métodos, mas qual tipo é retornado nos dois casos? Lembre-se tanto http.get quanto http.post retornam um observable stream, ou seja, ambos retornam o tipo Observable. Não podemos simplesmente indicar esse tipo de retorno, primeiro precisamos importar sua definição. Mas de qual módulo importá-la? Precisamos importá-la do módulo rxjs:
// alurapic/client/app/foto/foto.service.ts
import {Http, Headers} from '@angular/http';
import {Foto} from '../components/foto';
import {Observable} from 'rxjs';
export class FotoService {
http: Http;
headers: Headers;
url: string = 'v1/fotos';
constructor(http: Http) {
this.http = http;
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
}
lista(): Observable {
return this.http.get(this.url)
.map(res => res.json());
}
cadastra(foto): Observable {
return this.http.post(this.url, JSON.stringify(foto),
{ headers: this.headers });
}
}
No entanto, temos uma mensagem de erro, parece que o tipo que usamos é incompatível:
Generic type 'Observable<T>' requires 1 type argument(s).
import Observable
A classe Observable é genérica demais e precisamos indicar que tipo de dados ele está observando. No caso do método lista, o resultado da função map é uma lista de fotos, sendo assim podemos fazer:
// alurapic/client/app/foto/services/foto-service.ts
// código anterior omitido
lista(): Observable<Array<FotoComponent>> {
// código posterior omitido
Agora na versão mais enxuta que aprendemos:
// alurapic/client/app/foto/foto-service.ts
// código anterior omitido
lista(): Observable<FotoComponent[]> {
// código posterior omitido
Por fim, o o http.post no final das contas retorna um Observable. Será que é do tipo FotoComponent também? Que tal fazermos um teste e verificarmos se o compilador do TypeScript aprova nossa tipagem? Bem, ele não aprova e diz que o tipo esperado era Response. Precisamos importar essa classe do pacote angular/http. Nossa classe final ficará assim:
// alurapic/client/app/foto/foto.service.ts
import { Http, Headers, Response } from '@angular/http';
import { FotoComponent } from './foto.component';
import {Observable} from 'rxjs';
export class FotoService {
http: Http;
headers: Headers;
url: string = 'v1/fotos';
constructor(http: Http) {
this.http = http;
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
}
lista(): Observable<FotoComponent[]> {
return this.http.get(this.url)
.map(res => res.json());
}
cadastra(foto: FotoComponent): Observable<Response> {
return this.http.post(this.url, JSON.stringify(foto),
{ headers: this.headers });
}
}
Precisamos ainda adicionar FotoService ao módulo FotoModule. Contudo, dessa vez utilizaremos a propriedade providers para registrar nosso serviço:
// alurapic/client/app/foto/foto.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FotoComponent } from './foto.component';
import { FiltroPorTitulo } from './foto.pipes';
import { FotoService } from './foto.service';
@NgModule({
imports: [ CommonModule ],
declarations: [ FotoComponent, FiltroPorTitulo ],
exports: [FotoComponent, FiltroPorTitulo ],
providers: [ FotoService ]
})
export class FotoModule { }
O próximo passo é usarmos o serviço que acabamos de criar em ListagemComponent e CadastroComponent.