Alura > Cursos de Front-end > Cursos de Angular > Conteúdos de Angular > Primeiras aulas do curso Angular: componentização, formulários e interação com APIs

Angular: componentização, formulários e interação com APIs

Serviços e injeção de dependencia - Apresentação

Olá! Boas-vindas a mais um curso de Angular! Eu sou Vinicios Neves e vou te acompanhar nessa jornada.

Vinícios Neves é um homem de pele clara com rosto oval. Tem olhos castanho-escuros, sobrancelhas grossas, nariz comprido e boca larga. No rosto, tem barba e bigode, além de um óculos de grau com armação quadrada preta. No corpo, uma camiseta cinza. Está sentado em uma cadeira cinza. Ao fundo, uma parede lisa com iluminação em degradê nas cores azul e rosa.

Vamos evoluir o projeto Jornada Milhas. No curso anterior apresentado pela instrutora Nay e eu, implementamos a camada visual com a ajuda do Figma. Recomendamos que ele seja assistido antes deste, contudo, se não houver interesse, disponibilizaremos esse projeto base finalizado.

Com a camada visual implementada, focaremos em desenvolver o comportamento do formulário de busca.

Observando-o superficialmente, parece uma tarefa simples. Contudo, precisaremos compartilhar o estado entre as partes do formulário armazenadas em modais e em outros componentes. Para chegar a esse resultado, experimentaremos diversas situações diferentes, nas quais traçaremos estratégias para atingir nossos objetivos.

Além disso, subiremos o back-end e trabalharemos com requisições, buscando estados brasileiros, promoções e depoimentos.

Para quem é este curso?

Se você:

Te convidamos a embarcar conosco no Jornada Milhas mais uma vez e desenvolver toda a aplicação, tornando-a pronta para os próximos passos. Este é só mais um pedaço do que implementaremos nessa aplicação.

Te esperamos no primeiro vídeo!

Serviços e injeção de dependencia - Entendendo o providedIn

Para evoluir o Jornada Milhas, nos preocuparemos em criar a camada de comunicação que obterá dados de promoções da API (a fonte de verdade).

Instalando e configurando o projeto

Após clonar o projeto do curso anterior em uma pasta denominada "jornada-milhas" e alocar a API em uma pasta denominada "api-jornada-milhas", vamos abrir o terminal, por meio do qual acessaremos a pasta da API com o comando abaixo.

cd api-jornada-milhas

Em seu interior, rodaremos o comando abaixo para garantir que tudo está atualizado.

npm i

Para subir a API, rodaremos o comando abaixo.

npm run start:dev

Como resultado, a API ficará disponível na porta 8080. Vamos abrir uma nova guia do navegador e digitar o caminho abaixo para acessá-la.

localhost:8080/api

Em seu interior, veremos o título "Jornada Milhas 1.0" e uma lista de métodos disponíveis. Inicialmente, vamos focar no método que buscará promoções.

GET/promocoes

Essa documentação é o Swagger e nos permite experimentar a execução deste método para verificar seu funcionamento. Vamos clicar nele para abrir uma seção abaixo dele, no interior da qual buscaremos o botão "Try it out" (Testar) no canto superior direito.

Após o clique neste botão, será exibido abaixo dele um botão denominado "Execute" (Executar), ocupando toda a largura da tela. Após clicarmos nele, dentro do Swagger ocorrerá uma divisão que o transformará em dois botões: "Execute" à esquerda e "Clear" (Limpar)" à direita.

Abaixo de ambos, serão exibidas três seções: a primeira delas, intitulada "Curl" é a superior, dentro da qual veremos que ele executou uma requisição do tipo GET em localhost:8080/promocoes.

curl -X 'GET' \
    'http://localhost:8080/promocoes' \
    -H 'accept: application/json'

A seção do meio se chama "Request URL" (URL da requisição) e possui a URL utilizada.

http://localhost:8080/promocoes

Já a maior seção, abaixo da segunda, denomina-se "Server Response" (Resposta do Servidor). Nela, temos duas colunas: à esquerda, a coluna "Code" com o código de status "200"; à direita, "Details" (Detalhes), que possui o bloco intitulado "Response body" (Corpo da resposta). Este último trouxe a estrutura da promoção, constituída de um ID, um destino, a URL de uma imagem e o preço.

[
    {
    "id": 11,
    "destino": "Atacama",
    "imagem": "http://localhost:8080/public/atacama.png",
    "preco": 2500
    },
    {
    "id": 12,
    "destino": "Veneza",
    "imagem": "http://localhost:8080/public/veneza.png",
    "preco": 1500
    },
    {
    "id": 13,
    "destino": "Patagônia",
    "imagem": "http://localhost:8080/public/patagonia.png",
    "preco": 750
    },
*Trecho do retorno omitido*
]

Vamos conectar o Jornada Milhas nesse back-end. Para isso, voltaremos ao terminal, no qual abriremos uma nova aba. Nesta, voltaremos ao Desktop com o comando abaixo.

cd ..

Listaremos as pastas com o comando abaixo.

ll

Nos resultados, veremos as pastas "api-jornada-milhas" e "jornada-milhas".

drwxr-xr-x 21 vinny staff 672B Jun 28 20:39 api-jornada-milhas

drwxr-xr-x 15 vinny staff 480B Jun 28 20:28 jornada-milhas

Entraremos na pasta do curso anterior por meio de um cd nela.

cd jornada-milhas

Rodaremos o comando abaixo para instalar todas as dependências desse projeto.

npm i

Observação: O npm i constitui o atalho para o comando npm install.

Após a instalação dos pacotes, faremos um ng serve para subir o ambiente de desenvolvimento do Angular.

ng serve

Enquanto isso, abriremos uma terceira aba no terminal, onde solicitaremos a execução do VS Code a partir da pasta "jornada-milhas".

code .

Neste momento, temos a janela do VS Code, o terminal, cuja segunda aba está executando o Angular na porta 4200 e a primeira aba está executando a API. Temos que manter ambos rodando.

Voltando ao navegador, abriremos uma nova guia na qual acessaremos a URL abaixo

localhost:4200

Essa URL carregará a página inicial do Jornada Milhas, na qual temos modais ativados pelos filtros de categorias — por exemplo, na seção "Passagens", temos dois botões para as categorias "1 adulto" e "Econômica", respectivamente. Clicando em uma delas, um modal intitulado "Viajante" será exibido, onde temos a opção de selecionar e buscar uma dessas categorias.

Na mesma seção, abaixo dos botões, temos campos de busca e de data para configurar nossas viagens. Podemos ver que nosso projeto base funciona, contudo, ele é estático.

Se descermos a tela para a seção "Promoções" veremos vários cartões iguais, representando uma viagem para Veneza por R$ 500,00. Vamos mudar isso e trazer dinamismo para o projeto.

Voltando à guia do navegador com a página do Swagger aberta na porta 8080, veremos o retorno que já abordamos e que contém a estrutura de uma promoção — ou seja, nossa interface. Ela possui:

A partir dessas informações, voltaremos ao VS Code e acessaremos o explorador lateral, no qual percorreremos o caminho de pastas "src > app" e criaremos dentro desta última a pasta "core". Em seu interior, alocaremos tudo o que for global na aplicação.

Dentro de "core", criaremos outra pasta denominada "types", em cujo interior criaremos o arquivo types.ts, que alocará todas as interfaces.

Dica: Também é possível separar cada interface em um arquivo. Fica a critério de cada dev.

No interior do arquivo types.ts, criaremos uma interface de Promocao, exportando-a ao mesmo tempo, com o comando export interface Promocao. À sua direita, abriremos um bloco de chaves, dentro do qual receberá os atributos vistos:

export interface Promocao {
    id: number
    destino: string
    imagem: string
    preco: number
}

Com a forma da interface de Promocao criada, podemos pedir ao Angular que gere um serviço para realizar a camada de comunicação entre nossa aplicação e o back-end.

Gerando o serviço de Promoções

Voltando ao terminal, temos a terceira aba com acesso à pasta "jornada-milhas", parada e disponível. Dentro dessa pasta, pediremos para que o angular (ng) gere um serviço (g s).

Vamos solicitar que seja criada a pasta "services" (serviços) e o arquivo promocao dentro da pasta "core", por meio do comando core/services/promocao.

ng g s core/servicos/promocao

Pressionaremos "Enter" para rodar o comando acima. O terminal nos avisará que a geração foi efetuada, mas conferiremos no VS Code.

Por meio do explorador, expandiremos o caminho de pastas "app > core > services" e veremos dois arquivos:

No interior do arquivo de serviço, veremos que ele foi criado como Injectable — ou seja, podemos injetá-lo em nossas classes do Angular. Além disso, ele provém do root — isso significa que só haverá uma instância deste serviço para a aplicação inteira.

O nome deste padrão de projetos é Singleton — caso queira mergulhar neste assunto, disponibilizaremos nessa aula uma atividade sobre ele.

import { Injectable } from '@angular/core';

@Injectable({
    providedIn: 'root'
})
export class PromocaoService {

    constructor() { }
}

Com a base pronta e os serviços gerados, configuraremos a camada HTTP necessária para se comunicar com o back-end. Faremos isso a seguir.

Serviços e injeção de dependencia - Manipulando variáveis de ambiente

O Angular, similar a um canivete suíço com mil e uma utilidades, nos entrega a camada que abstrai as requisições HTTP necessárias para que o Jornada Milhas funcione integrado ao back-end.

Criando a camada de comunicação

Acessaremos o VS Code e, no interior do arquivo promocao-service.ts, injetaremos o nosso HttpClient entre os parênteses do constructor(). Vamos pressionar "Enter" neste local e adicionar o nível de acesso private junto a um httpClient com "h" minúsculo.

Vamos tipá-lo como HttpClient com "h" maiúsculo para que o Angular saiba o que queremos injetar aqui.

@Injectable({
    providedIn: 'root'
})
export class PromocaoService {

    constructor(
            private httpClient: HttpClient
        ) { }
}

Ao digitar "HttpClient", o VS Code abrirá uma lista de sugestões, entre as quais há a importação do HttpClient proveniente da rota "@angular/common/http".

Vamos selecioná-la, permitindo que seja importada no início do arquivo atual por meio do comando abaixo.

import { HttpClient } from '@angular/common/http';

Vamos salvar o código e voltar ao navegador, onde verificaremos a aba da aplicação na porta 4200 em busca do resultado.

Vamos recarregar a página e abrir a aba do inspecionador de elementos por meio do botão direito e da opção "Inspecionar Elemento". Acessaremos esse inspecionador na aba lateral direita, selecionando a guia do console e verificando que não há erros apontados.

Com isso, temos nosso HttpClient disponível para uso. Voltaremos ao VS Code e seguiremos para a próxima etapa.

No mesmo arquivo promocao-service.ts, abaixo do HttpClient, adicionaremos um método. Considerando que se trata de um serviço que trata exclusivamente de promoções, listando as disponíveis, podemos chamar esse método de listar().

@Injectable({
    providedIn: 'root'
})
export class PromocaoService {

    constructor(
            private httpClient: HttpClient
        ) { }
        
        listar() {
        
        }
}

Vamos tipá-lo, pedindo que retorne um Observable. Ao digitar "Observable", o VS Code abrirá uma lista de sugestões, entre as quais há a importação do Observable proveniente de "rxjs".

Vamos selecioná-la, permitindo que seja importada no início do arquivo atual por meio do comando abaixo.

import { Observable } from 'rxjs';

Esse Observable, no qual adicionaremos o Generics do TypeScript por meio dos sinais de menor e maior para dizer em seu interior que ele trabalha com Promocao. Este último é o type, ou seja, a interface que acabamos de criar.

O Promocao é um arranjo com uma lista de promoções, portanto, adicionaremos um par de colchetes à sua direita.

@Injectable({
    providedIn: 'root'
})
export class PromocaoService {

    constructor(
            private httpClient: HttpClient
        ) { }
        
        listar() : Observable<Promocao[]>{
        
        }
}

No interior das chaves de listar, adicionaremos um this.httpClient.get<Promocao>() que retornará um arranjo de promoção, por isso utilizaremos o Generics. À esquerda desta linha, adicionaremos um return.

Entre os parênteses, adicionaremos aspas simples, entre as quais informaremos a URL na qual a requisição será efetuada.

@Injectable({
    providedIn: 'root'
})
export class PromocaoService {

    constructor(
            private httpClient: HttpClient
        ) { }
        
        listar() : Observable<Promocao[]>{
            return this.httpClient.get<Promocao>('')
        }
}

Quando falamos em URL, temos a API rodando no navegador em "localhost:8080". No ambiente de desenvolvimento, esta é a URL utilizada.

Quando publicarmos a aplicação, é provável que o endereço mude. Pensando nisso, qualquer aplicação robusta, inclusive o Angular, entrega uma forma para lidar com essas variáveis de ambiente.

No navegador, voltaremos à guia aberta com a documentação do Angular, onde buscaremos a seção "Configuring application environments" (Configurando ambientes da aplicação).

Nela, o primeiro comando é solicitar ao ng que gere os ambientes.

ng generate environments

Vamos copiar esse código e voltar à terceira aba do terminal, que está disponível. Lembrando que temos outras duas abas rodando o front-end (Angular) e o back-end. Ambas não podem ser fechadas.

Vamos colar o código na aba disponível e pressionar "Enter". No retorno, o terminal dirá que o arquivo angular.json foi criado e atualizado.

UPDATE angular.json (3283 bytes)

Vamos voltar ao VS Code, rolar a aba do explorador para baixo e localizar a pasta "environments", dentro da qual encontraremos dois arquivos: environment.development.ts (o ambiente de desenvolvimento) e environment.ts (o ambiente produtivo).

Dentro de environment.development.ts, temos um bloco de chaves do export const environment. Entre as chaves, criaremos a apiUrl que receberá a string do endereço base da API. Vamos recolhê-la no Swagger.

Voltando ao navegador, na aba do Swagger, veremos o endereço "localhost:8080/api#/default/PromocoesController_findAll". Ou seja, usaremos a porta 8080.

A API mencionada nesse endereço se refere ao endereço da documentação do Swagger. Dentro da página em si, temos a seção "Request URL", onde veremos o exemplo com o endereço a ser utilizado: "http://localhost:8080/promocoes".

A partir do endereço de exemplo, copiaremos somente a base.

http://localhost:8080

Voltando ao VS Code, colaremos o conteúdo copiado entre as aspas simples de apiUrl, dentro do ambiente de desenvolvimento.

export const environment = {
    apiUrl = 'http://localhost:8080'
}

Por meio do explorador, acessaremos o arquivo environment.ts, referente ao ambiente produtivo. Entre as chaves de export const environment, adicionaremos a mesma apiUrl que receberá entre aspas simples a URL final — que, neste caso, será http://api.jornadamilhas.com.

Essa URL não existirá, por enquanto. Contudo, servirá para alcançar o objetivo de alinhar os arquivos, criando uma entrada de produção correspondente a cada entrada de desenvolvimento.

export const environment = {
    apiUrl = 'http://api.jornadamilhas.com'
}

Após criar os ambientes, temos que "derrubar" o Angular e subi-lo novamente, permitindo que ele saiba lidar com os novos elementos. Para isso, acessaremos a segunda aba do terminal, na qual pressionaremos "Ctrl+C" para parar a execução.

Vamos limpar esse terminal e rodar em seguida o ng serve para servir a aplicação.

ng serve

Durante este momento, ele sobrescreverá automaticamente o environment com o environment.development, permitindo o acesso à API http://localhost:8080 que funcionará somente em nossa máquina.

Com o acesso disponível, voltaremos ao serviço promocao.service.ts, onde criaremos um atributo private chamado apiUrl tipado como string. Ele será adicionado na primeira linha entre as chaves do export class PromocaoServico.

Vamos importar o arquivo environment. Para que o Angular funcione, sempre devemos importar este, e não o environment.development, portanto, selecionaremos na lista de sugestões do editor a opção "environment" proveniente do caminho "src/environments/environment".

Importação automática do VS Code:

import { environment } from 'src/environments/environment';

Se adicionarmos um ponto à direita de environment, o editor sugere apiUrl. Vamos selecioná-lo.

Código da classe atual:

@Injectable({
    providedIn: 'root'
})
export class PromocaoService {

    private apiUrl: string = environment.apiUrl;

    constructor(
            private httpClient: HttpClient
        ) { }
        
        listar() : Observable<Promocao[]>{
            return this.httpClient.get<Promocao>('')
        }
}

Por último, entre os parênteses de this.httpClient.get<Promocao>(''), retiraremos as aspas simples e adicionaremos um this.apiUrl e concatenaremos o /promocao. Para isso, adicionaremos crases e chaves ao redor de this.apiUrl. À esquerda da chave de abertura, adicionaremos um dólar ($).

Com isso, a string será interpolada. À direita da chave de fechamento, adicionaremos o restante da URL: /promocoes.

@Injectable({
    providedIn: 'root'
})
export class PromocaoService {

    private apiUrl: string = environment.apiUrl;

    constructor(
            private httpClient: HttpClient
        ) { }
        
        listar() : Observable<Promocao[]>{
            return this.httpClient.get<Promocao>(`${this.apiUrl}/promocoes`)
        }
}

Com essa configuração, temos uma forma de realizar a requisição HTTP. Tudo pronto para testarmos.

Por meio do explorador, buscaremos a pasta "pages", dentro da qual acessaremos o arquivo home.component.ts.

Em seu interior, entre as chaves de export class HomeComponent, criaremos um constructor() acompanhado de um bloco de chaves. Entre seus parênteses, configuraremos o modificador de acesso private denominado servicoPromocao que receberá o tipo PromocaoService.

export class HomeComponent {
    constructor(private servico Promocao: PromocaoService) {
    
    }
}

Ao digitar o tipo, o editor sugerirá a importação de PromocaoService proveniente de "src/app/core/services/promocao.service". Vamos selecioná-la, permitindo a importação automática dela através do comando abaixo.

import PromocaoService } from 'src/app/core/services/promocao.service';

À direita de HomeComponent, faremos um implements OnInit. Com isso, a classe HomeComponent será sublinhada em vermelho, apresentando um erro. Se posicionarmos o cursor em cima dele, o editor exibirá um diálogo avisando que, para implementar o OnInit, essa classe deve implementar o método ngOnInit.

Para implementá-lo, clicaremos no ícone de lâmpada azul, localizada acima da linha atual, no canto esquerdo. Ele abrirá uma caixa de sugestão, na qual selecionaremos "Implement interface 'OnInit'" (Implementar interface 'OnInit').

Após o clique, o VS Code construirá o método ngOninit() abaixo do construtor.

export class HomeComponent {
    constructor(private servico Promocao: PromocaoService) {
    
    }
    ngOnInit(): void {
        throw new Error('Method not implemented.');
    }
}

Ele não fará um throw new Error, por isso, deletaremos essa linha e a substituiremos por um this.servicoPromocao.listar().

export class HomeComponent {
    constructor(private servico Promocao: PromocaoService) {
    
    }
    ngOnInit(): void {
        this.servicoPromocao.listar();
    }
}

Vamos verificar no navegador se essa requisição será feita. Na aba da aplicação Jornada Milhas, recarregaremos a página e veremos um erro de muitas linhas na aba lateral do console, indicando que não há um provedor HttpClient e consequentemente o Angular não consegue realizar a entrega.

ERROR Error: Uncaught (in promise):

NullInjectorError: R3InjectorError (AppModule) [PromocaoService -> HttpClient-> HttpClient]:

NullInjectorError: No provider for HttpClient!

Retorno omitido

Isso ocorreu porque não configuramos o módulo HTTP.

Voltando ao VS Code, no explorador, acessaremos o arquivo app.module.ts dentro do caminho de pastas "src > app". Em seu interior, buscaremos o arranjo de imports, dentro do qual adicionaremos como último elemento um HttpClientModule.

imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatToolbarModule,
    MatButtonModule,
    MatCardModule,
    MatButtonToggleModule,
    MatIconModule,
    MatChipsModule,
    MatFormFieldModule,
    MatInputModule,
    MatDatepickerModule,
    MatNativeDateModule,
    MatDialogModule,
    HttpClientModule
]

Voltando ao navegador, veremos que o erro não aparece mais no console. Vamos recarregar a página e constatar que tudo funciona bem.

À direita da aba "Console", clicaremos na aba "Network", recarregaremos a página e veremos que a requisição não será feita.

Temos o serviço funcionando, realizamos as injeções, mas não conseguimos verificar a execução da requisição nem visualizar os dados. As seguir, veremos como resolver esse problema.

Sobre o curso Angular: componentização, formulários e interação com APIs

O curso Angular: componentização, formulários e interação com APIs possui 107 minutos de vídeos, em um total de 44 atividades. Gostou? Conheça nossos outros cursos de Angular em Front-end, ou leia nossos artigos de Front-end.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Angular acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas