O que é RxJs?
RxJs é uma biblioteca que fornece formas de trabalhar com programação assíncrona e baseada em eventos - reativa - usando observables. A biblioteca provém um tipo principal o Observable
, tipos satélites como Subjects
, Observers
e Schedulers
e também operadores para lidar a coleção de eventos assíncronos.
Recapitulando: o que são Observables?
O Observable
é a peça chave do Design Pattern Observer, baseado em eventos o padrão define Observable como o objeto de interesse e que pode ser observado, o mesmo guarda uma lista de interessados em suas mudanças - Observers ou Subscribers - e emite um evento avisando aos interessados todas as vezes que ocorrer uma mudança em seus itens.
Se ainda não ficou claro, recomendo a leitura do meu post sobre o Padrão Observer antes de continuar.
RxJs + Angular
O novo Angular foi construído em cima da programação reativa, o uso de Promises foi abandonado e fora adotado o uso de Observables, e ai que entra o RxJs.
Requisições HTTP
Vamos começar com o básico, lidando com respostas de requisições HTTP. Normalmente, fariamos usando Promises como acontece em React JS, porém em Angular vamos usar Observables do RxJS.
Criando nosso service
Ele que realiza a requisição e retorna um Observable do tipo HttpResponse<User>
import { Injectable } from "@angular/core";
import { HttpClient, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface IUser {
name: string;
id: string;
imgUrl?: string;
}
@Injectable({
providedIn: 'root'
})
export class UserService {
constructor(private httpClient: HttpClient) {}
getUser(id: string): Observable<HttpResponse<IUser>> {
return this.httpClient.get<HttpResponse<IUser>>(`/api/users/${id}`)
}
}
Consumindo
Nosso componente irá chamar o UserService
, e para receber a resposta da requisição precisamos realizar o subscribe
no Observable
retornado pelo método getUser()
, passando como parâmetro para o método subscribe
um objeto do tipo observer
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.scss']
})
export class UserComponent implements OnInit {
private user!: IUser;
constructor(private userService: UserService) { }
ngOnInit(): void {
this.getUserData();
}
getUserData(){
this.userService.getUser('1234').subscribe({
next: (data) => {
if(data.body) this.user = data.body
},
error: (err) => console.log(err)
})
}
}
Observer
O Observer é o consumidor dos valores emitidos pelo o Observable, ele nada mais é que um objeto que possui callbacks para cada evento do emissor. Exemplo:
const observer = {
next: x => console.log('Observer recebeu o prox valor: ' + x),
error: err => console.error('Observer recebeu um erro: ' + err),
complete: () => console.log('Observer completou o recibimento'),
};
Dentro de cada um desses callbacks podemos realizar o necessário com o dado emitido.
Eventos Pai -> Filho
O segundo caso que irei demostrar é quando precisamos que um componente filho reaja a um evento do seu pai.
Exemplo: enquanto a requisição para buscar o usuário não finaliza, quero que um componente filho mostre a mensagem "carregando" e quando finalizar ele mostra os dados normalmente.
Entendo o conceito de Subject
Enquanto Observables são unicast, ou seja, cada subscriber possui uma execução única e indepente (seu próprio "canal") do Observable pois cada subscribe()
invoca uma nova execução. Os Subjects são multicast, eles não invocam uma nova execução para cada subscribe()
, eles simplemente adicionam na lista de Observers e avisa a todos pelo mesmo "canal" quando houver uma mudança.
Criando um Behavior Subject
Para emitir eventos do pai para o filho, vamos utilizar Behavior Subject
, que consiste em um Subject que possui valor inicial e emite seu valor atual para cada Observer inscrito.
- Criamos um atributo
isLoading$
no componente pai
isLoading$: BehaviorSubject<boolean> = new BehaviorSubject(true);
- Ao completar a requisição, atualizamos o valor para false
getUserData(){
this.userService.getUser('1234').subscribe({
next: (data) => {
if(data.body) this.user = data.body
},
error: (err) => console.log(err),
complete: () => {
this.isLoading$.next(false);
}
})
}
Passando para o filho
<child-component
[name]="user.name"
[isLoading$]="isLoading$"
>
</child-component>
Recebendo no filho
import { Component, OnInit } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { UserService, IUser } from 'src/app/shared/services/user.service';
@Component({
selector: 'child-component',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {
@Input() name?: string;
@Input() isLoading$: BehaviorSubject<boolean>;
constructor(private userService: UserService) { }
}
Utilizando o Behavior Subject
Agora no template do componente filho vamos usar um condicional para saber quando mostrar "carregando".
Quando o valor atual for true
=> ele deve mostrar
Quando o valor atual for false
=> ele deve esconder a div
<div *ngIf="isLoading$ | async">
<p>carregando</p>
</div>
Note que utilizei o pipe async, um recurso fornecido pelo Angular, que automaticamente realiza o subscribe no Observable - nesse caso é nosso Behavior Subject
isLoading$
- e retorna sempre o último valor emitido. Se quiser saber mais sobre esse operador, recomendo a leitura da documentação do Angular.
Para usar o pipe async não esqueca de importar o CommonModule no módulo do seu componente.
Quando iniciei meus estudos em Angular, dois anos atrás, RxJs era um assunto bem conturbado para mim e existiam poucos conteúdos em português, mesmo ele sendo uma peça fundamental para construirmos aplicações mais robustas com Angular.
Então, resolvi compartilhar um pouco do que aprendi nesses anos para ajudar quem esteja começando agora, espero que você tenha gostado do artigo 😊
Seu feedback é bem vindo!
Top comments (0)