Angular: Http
Material completo: Iniciando os estudos com Angular
Http: Criando o projeto e instalando o bootstrap
Vamos criar a estrutura inicial do projeto, para isso, podemos utilizar o Angular CLI para criar, passando os parametros para já criar o arquivo de rotas, com os arquivos de estilo na extensão SCSS e sem os arquivos de teste.
ng new requests-http --routing --style=scss -SNesse projeto, também utilizaremos o Bootstrap, podemos instalar a versão mais recente utilizando:
npm i bootstrap @types/bootstrap
npm i @types/bootstrap -D
npm i ngx-bootstrapUtilizamos o ngx-bootstrap pois dessa maneira, não precisamos utilizar o JQuery e podemos utilizar os componentes do bootstrap diretamente com tags HTML.
E importamos o arquivo de estilização do Bootstrap em nosso arquivo de estilo global “styles.scss”.
@import '~bootstrap/dist/css/bootstrap.min.css'E já criamos nosso menu de navegação inicial, utilizando a estrutura e as classes do proprio Bootstrap. Além de criarmos nosso container e o <router-outlet> para renderização das nossas rotas.
Http: Simulando servidor REST (json-server)
Para simular nosso back-end e conseguirmos realizar requisições HTTP, precisamos instalar o json-server que cuidará dessa parte para nós, podemos instalar utilizando:
npm i json-server -g
npm i @types/json-serverDepois de criado, precisamos criar uma estrutura inicial json que servirá como banco de dados. Na raíz do projeto, podemos criar um arquivo db.json contendo a estrutura:
Depois de tudo configurado, basta executar o comando abaixo para iniciar nosso servidor.
json-server --watch db.json
// Servidor irá executar nessa URL:
http://localhost:3000/cursosCaso queira testar os métodos REST (Get, Post, Put, Delete), pode ser utilizada alguma extensão do VSCode ou utilizar aplicações como Postman ou Insomnia.
Http: GET — Listas registros
Antes de mais nada, precisamos criar nosso serviço, que ficará responsável por realizar as chamadas para nosso servidor. Basta criar um utilizando:
ng g s cursos/cursosApós a criação, podemos começar a escrever nosso primeiro método list(), utilizando o módulo de HTTP através de injeção de dependência.
Também podemos fazer a tipagem do retorno desse método de listar cursos, com o ID e nome que iremos receber do servidor.
export interface Curso {
id: number;
nome: string;
}Em nosso componente, podemos chamar o método list() de dentro do nosso serviço, lembrando sempre de realizar o subscribe(). Criamos uma propriedade cursos que receberá o array de cursos que temos.
Em nosso HTML, criamos uma estrutura basica de lista utilizando as proprias classes do Bootstrap. Realizamos um *ngFor em nossa tabela para que seja exibido todos os cursos em tela.
No final, teremos uma tabela parecida com essa, iremos implementar as funcionalidades no decorrer do módulo.
Http: Configurando variáveis de ambiente
Para não ficarmos consumindo diretamente a URL do nosso back-end, pensando que essa aplicação pode ir para produção e ficar exposta, podemos trabalhar com variáveis de ambiente.
Quando criamos nossa aplicação Angular, ele já cria uma pasta chamada “environment”, com os arquivos de ambiente local e produção. Criamos uma chave chamada API recebendo a URL do servidor.
Em nosso serviço, ao invés de passarmos a URL diretamente, podemos realizar a importação do environment e acessar a environment.API. A unica coisa que foi preciso passar foi o endpoint /cursos.
Http: GET + Pipe Async
Quando utilizamos subscribe(), como boa prática, precisamos nos desescrever dessas chamadas para que não tenhamos vazamento de memória.
Uma forma de não se preocupar com essas desinscrições, é utilizar o pipe async, sem se preocupar em realizar o subscribe() em nosso serviço.
Em nosso componente, criamos a propriedade cursos$ que será do tipo Observable. Em nosso ngOnInit(), apenas chamamos o método list() do nosso serviço sem realizar o subscribe().
Atribuímos o sifrão ($) devido a uma convenção da comunidade para saber que aquela propriedade é um observable.
Em nosso HTML, em nosso *ngFor, bastaria adicionar o pipe async que já estaria funcionando. Mas, precisamos trabalhar também com o *ngIf e toda a tabela só deverá ser exibida caso exista cursos.
Nesse cenário, se adicionassemos “cursos$ | async” nos dois lugares, o Angular iria realizar duas chamadas desnecessárias. Para isso, em nosso *ngIf utilizamos como “cursos$ | async as cursos”, ou seja, estamos dizendo que teremos uma chamada assincrona e que deve ser referenciada como cursos (variável de template).
Agora que temos nossa variável de template (local), podemos chama-la diretamente em nosso *ngFor.
Temos o mesmo resultado de antes, mas agora utilizando o pipe async, além de termos adicionado uma tela simulando o carregamento dos cursos.
Http: RxJS — Unsubscribe automático
Nessa aula, aprendemos a utilizar alguns recursos que o RxJS nos oferece para se desinscrever automaticamente após realizar as nossas requsições.
Para ter acesso a esse material, basta acessar esse commit, realizar o download dos arquivos e inserir no projeto.
- Componente sem unsubscribe: Após a emissão do evento, a conexão se mantém ativa, mesmo destruindo o componente, se emitirmos um novo evento ele é disparado.
- Componente com unsubscribe: Podemos criar uma propriedade do tipo Subscription e atribuir a o serviço a ele. No ngOnDestroy(), podemos chama-lo passando o unsubscribe().
- Componente com async: Já aprendido nos tópicos acima, criamos uma propriedade do tipo Observable, e utilizamos o pipe async. Após a chamada, o Angular se encarrega de cortar a comunicação.
- Componente com takeUntil: Um dos operadores do RxJS, pode ser utilizado dentro de um pipe, antes de chamar o subscribe(). Emite um novo observable, ainda é necessário escrever bastante código e passando ao ngOnDestroy().
- Componente com take: Uma das maneiras mais simples, basta chamar o operador take(1) dentro de um pipe e passar como parametro qual o valor de tentativas que deverá ser chamado aquele serviço. Após atender esses parametros, a conexão é finalizada.
Abaixo, temos um exemplo real e valores sendo emitidos para todos os componentes. Após a destruição dos componentes, quando clicamos novamente em emitir valor, somente o que não realizamos o unsubscribe consegue receber esse dado.
Http: Capturando erros (+Erro com async)
Nossa lista de cursos está sendo iniciada assim que o componente é renderizado, buscando em nosso servidor or cursos que temos cadastrados. Caso esse servidor esteja offline ou aconteça algum problema na requisição, ficaremos com a mensagem “Carregando cursos…” travada em tela.
Precisamos avisar para o usuário que não foi possivel carregar a lista de cursos. Em nosso HTML, criaremos um novo <ng-template>, com um *ngIf, recebendo o observable “error$” e o pipe async.
Em nosso componente, criaremos a propriedade “error$” que será um Subject, um tipo de observable que consegue enviar valores para muitos observaveis.
Em nosso componente, podemos fazer a captura dos erros chamando dentro do pipe, o operador catchError(). O mesmo recebe uma função para podermos realizar nossa lógica, podemos aproveitar e emitir true para nosso error$.
Assim, por padrão será exibido nossa tela de carregando de cursos, mas caso aconteça algum erro no caminho, emitimos nosso erro, e é alterado para a mensagem de falha no serviço.
Quando desligamos nosso servidor podemos observar que apesar de mostrar que os cursos estão carregando, quando nosso serviço falha, o erro é capturado e é exibido a mensagem de falha em tela.
Http: Alerta de Erro com Bootstrap e serviço genérico
Vamos alterar a mensagem simples que temos hoje em nosso HTML quando não é possivel carregar a lista de cursos. Iremos implementar os alertas que temos no Bootstrap, juntamente com uma modal com o NGX-Bootstrap.
Antes de qualquer coisa, como esse componente será de uso compartilhado por toda a aplicação, criaremos um novo módulo chamado Shared.
ng g m sharedPrecisamos importa-lo em nosso App.Module, para que todos os componentes/serviços criados, esteja disponivel para o restante da aplicação.
Agora, criaremos o componente de alerta, onde ficará toda a lógica e HTML/CSS do nosso componente.
ng g c shared/alert-modalNo componente de alerta que acabamos de criar, vamos começar a estruturar nosso HTML. Pegamos de exemplo os próprios alertas do Bootstrap, que contém o alerta + botão de fechar.
As propriedades type e message criaremos posteriormente para controlar o que será exibido no alerta e a função onClose(), ficará responsável por fechar nossa modal.
No nosso component, precisaremos declarar o message e o type, ambas recebendo o decorator @Input() para receber os dados de fora. No método onClose(), estamos chamando o bsModalRref, é a referência do nosso modal, executando a função hide(), para fecha-lo. A documentação de como utilizar está em NGX-Bootstrap Modals.
Pensando que poderemos utilizar esse modal em vários lugares da aplicação, criaremos um serviço, que irá criar as instâncias necessárias, passando os dados e requisitando a abertura do nosso AlertModalComponent.
Temos um método genérico showAlert(), responsável por criar a referẽncia da modal e passar o conteúdo (type/message). E também criamos os mais especificos como o showAlertDanger(), que chama o showAlert() e já passa também o tipo correto, assim, temos um método mais semântico.
Finalizamos toda a estrutura do nosso componente de alerta, em nosso componente de cursos, sempre que falhar alguma requisição, podemos chamar nosso serviço e o método correspondente, exemplo: alertService.showAlertDanger().
Http: Criando formulário para criar e editar cursos
Precisamos criar nosso componente que servirá como formulário para criar novos cursos ou edita-los. Podemos criar utilizando:
ng g c cursos/cursos-formEm nosso componente, começaremos criando nosso formulário, importando o FormBuilder e criando nosso formGroup. No nosso caso, teremos apenas o campo “nome”, passando alguns validadores.
Os métodos onSubmit() e onCancel() também foram criados para utilizarmos posteriormente no HTML.
Em relação ao HTML, criamos toda a estrutura fazendo uso do Bootstrap 5, atribuindo o formControlName no input correto e os botões Salvar/Cancelar executando s métodos que criamos.
Afim de dar um feedback visual ao usuário, também criamos as mensagens de erro, capturando no momento da digitação.
Lembrando que como criamos um novo componente, precisamos informar quando ele será exibido. Criamos dois novos paths que utilizarão o mesmo componente.
- novo — Utilizado na criação de novos cursos
- editar/:id — Utilizado na edição de um curso, recebendo um id como parametro
No final, teremos esse resultado.
Http: POST — Criando cursos
Já temos em nosso serviço, o método list() para buscar todos os nossos cursos. Agora, criaremos o create(), responsável por criar os cursos em nosso banco.
Utilizando o Http Client, podemos utilizar o método post(), passando a URL do serviço e o curso como parametro. Como não precisamos ficar observando o retorno, não é necessário termos o subscribe(). Temos apenas um pipe() com take(1) para realizar somente uma tentativa de envio por vez.
Agora, em nosso componente, no método onSubmit() que haviamos criado anteriormente, podemos realizar o subscribe() e realizar as devidas tratativas de sucesso e erro. Nesse caso, estamos utilizando nosso modal que também criamos nos tópicos anteriores.
Http: Editando cursos e Observables aninhados
Para conseguirmos editar um curso, precisamos atribuir uma função ao botão “Atualizar”, criaremos com o nome onEdit() recebendo o ID do curso. Como estamos iterando uma lista de cursos, já temos essa informação.
Precisamos criar agora o método onEdit(), a unica coisa que faremos é redirecionar para a rota /editar/:id que criamos anteriormente passando o ID do curso. Se fizermos somente isso, o Angular irá se perder, pois irá montar a rota a partir do localhost:4200/.
Precisamos que ela seja criada a partir de localhost:4200/cursos/, para isso, utilizamos mais um argumento, chamando relativeTo.
- Sem usar o relativeTo: http://localhost:4200/editar/1
- Usando o relativeTo: http://localhost:4200/cursos/editar/1
O próximo passo, é criarmos nossa função de atualizar cursos. No nosso cursos.service, criaremos o método loadByID(id), ficará responsável por receber o ID do curso e buscar em nossa API.
Lembrando que utilizamos take(1) para realizar apenas uma tentativa de busca e já se desinscrever automaticamente do susbscribe.
Com o método criado, podemos começar a trabalhar no cursos-forms.component. O primeiro passo é criarmos mais um formGroup chamado “id”. Não utilizaremos no HTML, mas para auditoria posteriormente.
No ngOnInit(), utilizaremos o route.params do módulo ActivatedRouter,é um observable onde podemos recuperar o “params” com a propriedade ID. Com isso, podemos chamar o método loadByID do nosso service passando o ID e que também é um observable.
Note que agora teremos observables aninhados, no trecho de código comentado podemos visualizar bem essa situação. Não ha problemas fazer dessa maneira, apenas ficaŕa um código mais extenso dependendo da quantidade de observables que você possuí.
Mas o RxJS nos fornece alguns métodos para auxiliar nesses casos, que é o caso do map e switchMap.
- map: Irá recuperar o ID do params e irá retornar o valor (nesse caso o ID) para a proxima função.
- switchMap: Recebe o valor do map anterior, no caso, nosso ID e chamada o método loadByID(ID)
O switchMap apesar de também retornar dados assim como o map, ele é utilizado pois também retorna um observable, assim, é necessário aguardar o retorno desses dados para continuar o fluxo.
Por fim, podemos nos subscrever normalmente, afim de receber os dados finais do nosso curso, passando para o método updateForm().
O método updateForm() que também vamos criar, apenas atualiza nosso formulário utilizando patchValue com o ID e nome do curso recuperado.
Após a contrução de toda essa parte, teremos como resultado o comportamento abaixo:
Http: Editando cursos + Resolver (Rota)
Quando clicamos em ‘atualizar’ e a rota é alterada, podemos ver uma rápida atualização (layout shift) no campo do nome do curso. Isso porque a consulta do curso pelo ID, está sendo realizada dentro do nosso método ngOnInit(). Para resolver essa questão, precisamos utilizar o guarda de rota ‘Resolver’ para que já tenhamos os dados carregados assim que carregar o componente.
Vamos criar uma nova pasta ‘guards’ dentro de ‘cursos’ e gerar um novo resolver:
ng g g cursos/guards/curso-resolverAssim que nossa guarda for criada, ela terá alguma interface padrão criada, podemos apagar e implementar a interface Resolve.
No método resolve(), temos como retorno um possivel objeto de Curso ou um Observable<Curso>. Como utilizaremos esse resolver tanto para editar um curso, como criar um novo curso, criaremos uma condição.
Caso nosso route tenha um params e esse seja o ID, iremos chamar nosso método loadByID, passando o ID que recuperamos da rota. Caso contrário, entendemos que estamos criando um novo curso, então retornamos um objeto com as propriedades nulas.
Certo, criamos nosso resolver, mas precisamos começar a utiliza-lo, para isso, em nossas rotas, podemos ter a propriedade ‘resolve’, que daremos o nome de ‘curso’, onde recuperamos posteriormente os dados guardados.
Em nosso cursos-form.component, não precisamos mais realizar a chamada no nosso ngOnInit(), diretamente do nosso construtor, iremos acessar o route.snapshot.data[‘curso’], sendo ‘curso’ o nome que demos lá em nosso arquivo de rotas.
Com os dados em mãos, assim que vamos inicializar nosso formulário, já podemos passar preenchendo seus valores.
Note que agora, quando nosso formulário é carregado, os valores já estão preenchidos, não sendo mais possível visualizar aquela mudança repentina dos valores.
Http: PUT Atualizando cursos
Vamos implementar agora nossa função de atualizar cursos e passar refatorando o código de criar cursos que temos no momento.
Em nosso cursos.service, podemos ver que o método create (que já tinhamos anteriormente e o método update (que criamos para atualizar nossos cursos) recebem o mesmo parametro, mudando apenas as rotas.
Vamos transforma-los em um método privado e somente quem irá chama-los será o método save(), recebendo o curso e verificando se temos ID ou não, para saber se devemos criar um editar um curso.
Em nosso cursos-form, dentro do onSubmit(), podemos fazer uma verificação e ver se nosso formulário possuí ID, se tiver chamamos os serviços correspondentes, da mesma forma que fizemos na hora de criar um curso (parte do código comentada).
Vamos refatorar nosso onSubmit(), como em nosso serviço criamos o método save(), podemos reutilizar a chamada para os dois casos (criação/edição), apenas atualizando a mensagem para que seja condizente em cada tipo de chamada.
Http: Popup de confirmação para remover cursos
Para finalizar nosso CRUD, vamos implementar nossa função de deletar um curso, implementando também uma modal para confirmação.
Primeiro, precisamos criar o método remove() dentro do nosso cursos.service para executar o http.delete passando a URL e o ID do nosso curso.
No nosso cursos-lista.html, precisamos atribuir uma função ao botão de remover curso, podemos criar o método onDelete(curso) passando a referência do nosso curso.
Precisamos criar a estrutura da nossa modal de confirmação, para isso, vamos utilizar uma base que o proprio NGX-Bootstrap nos disponibiliza.
Em nosso cursos-lista.component, precisamos criar algumas propriedades que servirá como auxilio na criação das funções.
- deleteModalRef: É uma referência a instância que criaremos da modal, sendo possivel utilizar métodos para exibir e fechar a modal.
- @ViewChild(‘deleteModal’) deleteModal: É uma forma de criarmos uma referência do nosso ng-template #deleteModal que criamos em nosso html.
Para finalizar, precisamos criar os métodos para dar ação ao botão de remover e os controles da modal.
- onDelete(): Irá criar uma referência do curso atual que foi selecionado para remoção. Assim como irá criar uma nova instância de uma modal e atribuindo ao nosso deleteModalRef.
- onConfirmDelete(): É o método que será executado caso clicarmos em “Sim” na modal. Com isso, podemos chamar o deleteModalRef.hide() para fechar a modal e chamar a função remove() do nosso cursosService passando o ID do curso a ser removido.
- onDeclineDelete(): Não fazemos nenhuma ação de deletar curso, apenas chamamos o método hide() do deleteModalRef, fechando a nossa modal.
Com isso, agora nossa aplicação já consegue realizar a remoção dos cursos e nossa modal está funcionando perfeitamente.
Http: Popup de confirmação genérica Bootstrap 4 (com RxJS)
A popup que criamos utilizando o NGX-Bootstrap é muito simples, vamos criar uma genérica, baseada no Bootstrap 5, para que possamos também definir o título e a mensagem da modal.
Get Rafael Simionato Zorzenon’s stories in your inbox
Join Medium for free to get updates from this writer.
Vamos criar um novo componente, que será nossa modal de confirmação:
ng g c shared/confirm-modalO nosso HTML é o mesmo que o Bootstrap nos forneceu, passamos apenas colocando nossas variáveis (message, cancelTxt e confirmTxt) pois serão personalizaveis e as funções de onClose() que criaremos posteriormente.
Para o confirm-modal.component, também utilizaremos o BsModalRef para criar uma referência desse modal e conseguir manipula-lo depois.
As nossas propriedades serão do tipo @Input() pois ela receberá esses dados no momento da criação da modal.
Precisamos ter uma forma de receber em outra parte da aplicação a resposta de confirmação dessa modal, para isso, vamos utilizar a propriedade confirmResult sendo um Subject<boolean> retornando um boolean.
No alert-modal.service, criaremos o método showConfirm(), passando os dados para o nosso popup de confirmação.
Também precisamos criar uma referência da nossa modal, que salvaremos na propriedade bsModalRef, e passaremos preenchendo os dados acessando a propriedade content.
Por fim, retornaremos o confirmResult, um booleano para sabermos se cancelamos ou confirmamos nossa ação.
No cusos-lista.component, seguimos aplicando a lógica do nosso onDelete(), na propriedade result$ atribuimos nosso alertService.showConfirm() passando os dados do popup como titulo e descrição.
Transformamos em um observable() para que possamos fazer uso dos operadores rxJS e o success/error.
Agora temos nosso modal de confirmação genérico funcionando e recebendo os dados corretamente.
Http: Criação de formulário para upload de arquivos
Vamos criar um novo módulo que servirá para o upload de arquivos na aplicação. É opcional e apenas para organização.
ng g m upload-fileCom o módulo criado, precisamos criar nossa nova rota no app-routing.module e dizer para o Angular qual o módulo será carregado ao acessar essa rota.
Agora, criaremos nosso componente que terá o formulário para upload dos arquivos, podemos cria-lo com:
ng g c upload-file/upload-filePodemos utilizar de exemplo o campo de upload que temos no Bootstrap 5, seguindo a própria documentação. Fazendo somente esses passos, nosso campo de upload já estará funcionando devido a versão do bootstrap não utilizar mais jquery.
Caso utilize uma versão de Boostrap anterior a 5 e que dependa do Jquery para funcionar, ao tentar realizar o upload de algum arquivo, nada acontecerá.
No curso, aprendemos a criar nossa própria função, que irá acessar a propriedade do campo e verificar se existe arquivos e exibir o nome deles no campo. Lembrando que essa parte é opcional.
Abaixo, segue o resultado do nosso componente que criamos e já realizamos o upload de uma imagem chamada code.png.
Http: Upload de arquivo no back-end com NodeJS
Para conseguirmos salvar nossos arquivos, precisamos criar nosso back-end, como o foco do curso é Angular, criaremos algo simples com NodeJS e salvaremos os arquivos no proprio projeto front-end.
Precisamos criar uma nova pasta, pode ser na raiz do projeto e iniciar um novo projeto node com “npm init”. Após isso, vamos instalar nossas dependencias:
npm i -save express body-parser connect-multiparty corsApós iniciar o projeto node, instalar as dependencias e criar um novo comando de script para executar nosso servidor, teremos o resultado abaixo.
Abaixo, o código do nosso servidor, com as configurações de inicialização, liberação do cors, configuração para criar um novo diretório para salvar os uploads realizados, etc…
Http: Upload de arquivo — Request com FormData
Com nosso “back-end” criado, precisamos criar a ação de upload em nosso formulário. Começaremos criando um botão de upload onde chamará o método onUpload() e o mesmo estará desabilitado caso não tenha arquivos selecionados.
Em nosso componente, vamos criar uma propriedade chamada “files” do tipo Set<File>, essa é uma interface do próprio JavaScript, ele permite termos uma coleção de objetos, mas os valores devem ser unicos, ou seja, caso selecionarmos 2 arquivos com o mesmo nome, será considerado somente um, para não haver duplicação.
No nosso onChange(), precisamos popular essa propriedade “files” com nossos arquivos, aproveitamos que já temos um “for” realização a iteração e adicionamos todos esses arquivos com files.add().
Vamos criar o método onUpload(), realizando algumas verificações de segurança e chamando o serviço de upload que criaremos posteriormente passando nosso array de objetos e a URL do nosso back-end.
Agora, vamos criar nosso serviço, que irá realizar a chamada de fato ao nosso back-end, podemos criar utilizando o comando:
ng g s upload-file/upload-fileComo esse serviço será genérico, nosso método receberá nossa lista de arquivos e a URL para envio dos arquivos.
Criamos nossa propriedade formData do tipo FormData(), um tipo que permite que tenhamos um conjunto de várias posições com chave/valor. Precisamos iterar sobre nossa lista de arquivos e adiciona-los individualmente em nosso formData.
Depois, vamos criar nossa request com new HttpRequest(), lembrando que também é possível utilizar o http.post do Angular, mas é só uma maneira de aprendermos uma forma nova.
Abaixo, podemos ver que nosso upload já está sendo feito e o arquivo sendo salvo no diretório que informamos em nosso back-end.
Http: Removendo CORS com Angular Proxy
Em nosso back-end, fizemos a liberação do CORS para que duas aplicações em portas diferentes conseguissem se comunicar. É legal para o desenvolvimento local mas não é uma boa prática ter isso em produção.
Podemos remover a importação e a configuração do CORS que temos atualmente:
Na raiz do nosso projeto Angular, podemos criar um arquivo chamado proxy.conf.js que receberá a configuração abaixo:
Caso preferirem, tambémé possivel criar a mesma configuração, mas com um arquivo de extensão json.
No nosso package.json, no script de “start” podemos adicionar uma nova flag informando que deve ser utilizado nosso arquivo de proxy. Agora, para iniciar o projeto, não utilizaremos mais “ng serve” e sim “npm run start” ou “yarn start”.
Http: Upload arquivo — Barra de progresso + Observando eventos Http
Até o momento, temos o nosso formulário de upload funcionando e o upload sendo feito corretamente. Mas e se quisermos dar um feedback para o usuário? Dependendo do tamanho do arquivo, esse envio pode demorar e para isso precisamos de uma maneira de mostrar que o arquivo está sendo carregado.
Vamos utilizar a barra de progresso do próprio Bootstrap 5, podemos copiar o código do exemplo e alterar os valores para a nossa propriedade ‘progress’ que criaremos posteriormente.
No nosso serviço, precisamos realizar algumas mudanças, ao invés de passar somente a URL e o formData, podemos passar um terceiro argumento que recebe um objeto, e queremos observar todos os eventos e o progresso atual do nosso upload.
Somente é possível verificar o progresso atual de carregamento para casos de upload/download. Métodos com CRUD básicos como GET/POST/PUT/DELETE não é possivel.
Se dermos um console.log() no response, em nosso componente, podemos visualizar agora todos os passos que o browser realiza para enviar nosso arquivo.
Um deles é do tipo 1 (Upload progress), onde contém a propriedade loaded (o quanto o browser já processou do arquivo) e o total, tamanho total do arquivo.
Existem outros tipos de Eventos HTTP e podem ser visualizados importando o tipo HttpEventType e clicando com o botão direito para acessa-lo.
No nosso método onUpload(), em nosso subscribe, ao invés de receber diretamente o response, estamos recebendo os eventos HTTP. Caso seja do tipo 4 (Response), significa que o arquivo foi carregado e está pronto para ser enviado ao nosso back-end.
Caso seja do tipo 1 (UploadProgress), podemos realizar um calculo de porcentagem para verificar o quanto do arquivo já foi processado. Após o calculo, salvamos na propriedade “progress” para utilizar-mos no HTML.
Abaixo, nossa barra de progresso funcionando, por conta da velocidade da rede, o arquivo conseguiu salvar de 0 a 100% em um único evento.
Http: Criando operador RxJS customizado
Nosso código para monitorar o progresso de upload de arquivos foi feito, mas se precisarmos utilizar em outros lugares da aplicação, precisaremos repeti-lo novamente.
Para isso, vamos criar nossos próprios operadores utilizando RxJS e poderemos reutilizar em qualquer lugar da aplicação.
Vamos criar um novo arquivo na pasta shared, onde criaremos duas funções:
- filterResponse: Irá receber os eventos da nossa requisição HTTP e filtrará somente a que nos interessa que é o response, com o corpo final da requisição. Com o map, podemos pegar o response e retornar para o proximo fluxo da aplicação.
- uploadProgress: Estamos passando uma função de callback como parametro, para que possamos manipular os dados em nosso componente. Também estamos verificando se o tipo do eventoé de UploadProgress, se for, fazemos o calculo da porcentagem.
Agora, no nosso componente, podemos refatorar nosso código, passando todos os operadores customizados dentro do nosso pipe. E no subscribe, receberemos todo o response já tratado.
E o resultado é o mesmo, interceptando o quanto já foi carregadod o nosso arquivo e no final, temos a resposta com o upload concluido e o corpo.
Http: Download do arquivo
Vamos criar duas novas rotas no index.js do nosso servidor, o node possuí o próprio método de download que utilizaremos para devolver o arquivo que desejamos.
Esses arquivos foram criados dentro da pasta uploads, o conteúdo está vazio, é apenas para testarmos o funcionamento da aplicação.
server
|-uploads
|---report.pdf
|---report.xlsxEm nosso HTML, podemos criar dois novos botões, que serão responsáveis por disparar as funções de download desses arquivos, as funções chamadas no evento de click, serão criadas posteriormente.
Em nosso serviço, criaremos dois métodos:
- download(): Responsável por fazer a chamada GET para nosso end-point. O retorno dessa requisição será um blob (campo com dados binários do nosso arquivo), mas podemos lê-lo como um json, para poder manipula-lo posteiormente.
- handleFile(): Irá criar uma nova instância do tipo Blob, depois criaremos um novo ObjectURL, forma do browser criar uma DOMString, uma string referenciando esse objeto. Depois criamos um elemento de link (não ficará visivel para o usuário), atribumos nosso blog e damos um click(). Com isso, o browser consegue realizar o download do nosso arquivo.
Agora em nosso componente, basta criarmos as funções que serão passadas para nossos botões, ambas irão consumir as funções que criamos em nosso serviço.
Esse é o resultado que temos, realizando os downloads corretamente. No exemplo da aula é mostrado como dar suporte ao IE/Firefox, mas no momento do estudo, o IE já perdeu totalmente o suporte e o código funcionou normalmente no Firefox.
Http: Criando tela de pesquisa
Vamos criar um novo módulo para trabalharmos nessa nova funcionalidade, podemos criar com:
ng g m reactive-searchApós a criação, basta realizar a importação do módulo e criar uma nova URL para esse módulo em app.routing.
{
path: 'busca-reativa',
loadChildren: () =>
import('./reactive-search/reactive-search.module').then((m) => m.ReactiveSearchModule),
},Precisamos criar nosso componente onde ficará todo o formulário com a tabela dos registros, podemos criar com:
ng g c reactive-search/lib-searchO código HTML, iremos utilizar uma estrutura pronta, podemos utilizar um exemplo disponivel no Bootsnipp, basta copiar o HTML e o CSS fornecido no exemplo.
Também já atribuimos um formControl chamado queryField para o campo de input, e criamos um botão com o evento de click com a função onSearch().
Em nosso componente, só criamos a estrutura inicial, com nosso formControl (como é somente um campo, não precisamos criar um formulário completo) e o método onSearch().
Até o momento, temos esse resultado, posteriormente criaremos a funcionalidade de pesquisa.
Http: Passando parâmetros na URL (HttpParams)
Agora, vamos utilizar uma CDN externa para buscar bibliotecas javascript,é com ela que iremos popular nossa tabela dependendo do valor que pesquisarmos.
Criamos três propriedades:
- SEARCH_URL: É só uma propriedade para salvarmos a URL da CND e utilizarmos em nossas funções.
- result$: É um observable que irá receber o response com todas as bibliotecas encontradas e também utilizaremos ela no HTML para percorrer os valores e preencher em tela.
- total: Receberá o total de registros encontrados para exibir a soma em tela.
Dentro do nosso método onSearch(), começaremos a desenvolver nossa consulta da mesma forma que já fizemos anteriormente, utilizando o http.get(). Dessa forma, a API irá trazer todos os registros, o que seria muito custoso para nossa aplicação. Iremos começar a aplicar então parâmetros para buscar somente resultados que condiz com o que estamos pesquisando.
Essa API, ela pode receber dois tipos de parâmetros:
- fields: São os campos que devem ser retornados, como o nome da biblioteca, versão, descrição, etc…
- search: É o termo que estamos pesquisando e os resultados serão devolvidos com base nisso.
No curso, também nos foi apresentado 3 maneiras de passar esses parâmetros:
- Forma 1: Podemos criar um objeto, passando as chaves e os valores que queremos passar como parâmetro.
- Forma 2: A mais correta, utilizando o próprio HttpParams(), montando nossos parâmetros aos poucos e o Angular irá se encarregar de montar a URL final.
- Forma 3: A forma menos recomendada, apesar de funcionar, é mais trabalhoso e o desenvolvedor precisa ficar se preocupando em montar URL e concatenar os valores corretamente.
Após montar toda a lógica do nosso componente, podemos começar a popular nosso HTML com essas informações, com as propriedades que criamos.
Como o result$ irá conter todos os valores das bibliotecas, podemos iterar sobre ele e utilizar o pipe async, para o Angular se desescrever automaticamente depois.
Abaixo, temos o resultado do nosso template com as pesquisa funcionando, somente quando há dados a serem pesquisados.
Http: Pesquisa/Busca com Programação Reativa
Aprendemos no tópico sobre formulários reativos, que um controle de formulário também é um observable, com isso, podemos ficar monitorando sempre que esse campo tiver alguma mudança de valor, utilizando o valueChanges.
Legal, mas se dispararmos uma requisição sempre que houver uma alteração no campo, teremos requisições disparadas a cada caracter digitado e também sendo disparado quando colocamos espaços em branco.
Logo, precisamos realizar alguns tratamentos, e todos eles estão sendo explicados na ordem em que foi escrito.
- map: No primeiro map, estamos pegando o valor atual do campo e realizando um trim(), não queremos campos sujos e com espaço.
- filter: Só iremos prosseguir com a busca caso o valor digitado tenha pelo menos 2 caracteres, ou seja, o length dessa string seja maior que 1.
- debouceTime: Não queremos que seja feita requisições a cada caracter digitado, então utilizamos o debounce para atrasar um pouco esse fluxo em 300ms, tempo suficente para digitarmos pelo menos um termo a ser pesquisado.
- distinctUntilChanged: Caso o valor digitado seja o mesmo que o anterior, não permite avançar o fluxo, pois entende-se que não houve mudança de valores no campo.
- switchMap: Precisamos realizar a requisição a nossa API e retornar um novo observable para que possamos manipula-lo nas próximas etapas, por isso, utilizamos o switchMap.
- tap: Com o tap, apenas executamos ações, nenhum valor é retornado. Nesse caso, só estamos salvando o total de registros no “this.total”, não é necessário retornar e salvar no results$.
- map: Agora sim, precisamos retornar todos os registros encontrados para o nosso results$, pois utilizaremos para iterar e popular nosso HTML, por isso utilizamos o operador map.
Temos o resultado final da aplicação, com as requisições sendo feitas a cada termo digitado, não é mais necessário termos o botão pesquisar. Lembrando que são alternativas e deve ser levado em conta o objetivo do seu projeto.










