saber_de_detalhes_pode_ser_ruim - VWJavascript/Alurapic GitHub Wiki

Saber de detalhes pode ser ruim

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.

Criando nosso primeiro serviço

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.

⚠️ **GitHub.com Fallback** ⚠️