This project was generated with Angular CLI version 15.1.6.
Sumário
Sobre Operadores |
Como utilizar Operadores |
Observações de Operadores |
Sobre Subjects |
Como utilizar Subjects |
Observações de Subjects |
Conclusão
Os operadores são um recurso da biblioteca rxjs e servem para transformar dados observáveis antes de mostra-los na aplicação/antes de informar ao subscribe(). Isso pode ser feito manualmente dentro das funcões do subscribe(), ou até mesmo dentro de um observável caso o tenha criado, mas a medida que a lógica que envolve os dados observáveis vai ficando mais complexa em uma aplicação, os operadores podem ser uma boa opção.
É possível implementá-los a partir do método -> pipe(); todo observável tem um método deste. O pipe pode ser imaginado como uma tubulação para os dados observados. Você deve importar o operador que irá utilizar na sua tubulação de 'rxjs/operators', veja um exemplo abaixo:
import { map } from "rxjs/operators";
No exemplo acima, o operador utilizado é o map. Ele deve ser declarado no parâmetro do pipe() e, em seu parâmetro, assume uma função como argumento, veja o exemplo abaixo:
getCurrencyQuote() {
return this.http.get('https://economia.awesomeapi.com.br/last/USD-BRL,EUR-BRL,BTC-BRL').pipe(map((data: any) => <-----{<-------} ))
}
Essa função recebida como argumento no operador map(), recebe os dados observados que passaram pela tubulação em seu parâmetro:
getCurrencyQuote() {
return this.http.get('https://economia.awesomeapi.com.br/last/USD-BRL,EUR-BRL,BTC-BRL').pipe(map((data: any <-----) =>{}))
}
Dentro desta função, podemos transformar os dados observados para que em seguida o método subscribe() possa ser chamado mostrando os dados transformados em algum lugar da aplicação:
getCurrencyQuote() {
return this.http.get('https://economia.awesomeapi.com.br/last/USD-BRL,EUR-BRL,BTC-BRL').pipe(map((data: any) => {
return [this.usd = {
code: data.USDBRL.code,
highPrice: this.formatPricesInBRL(data.USDBRL.high) ,
lowPrice: this.formatPricesInBRL(data.USDBRL.low),
percentageVariation: parseFloat(parseFloat(data.USDBRL.pctChange).toFixed(2)),
saleValue: this.formatPricesInBRL(data.USDBRL.ask),
buyValue:this.formatPricesInBRL(data.USDBRL.bid) }
...]}))
}
Neste exemplo de código o operador map() foi utilizado para mapear os dados do observável retornado pelo método get(), conforme demonstrado na documentação Angular, e para cada dado retornado foram criados três objetos, os quais seguem o padrão da interface CoinPrice, e retornado um array com esses objetos.
1 - O pipe() pode receber um ou mais operadores em seu parâmetro. Eles são executados um após o outro fazendo diferentes transformações nos dados observados.
2 - Imagine que o operador map() é semelhante a função map() utilizada para percorrer arrays. Para cada um dos dados que passar pelo pipe() e chegar no parâmetro da função assumida como argumento do operador map(), essa função vai fazer alguma transformação. Em sentido contínuo, também existe o operador filter(), que filtra os dados com base em uma condição.
Os subjects são parecidos com EventEmitter/emissão de eventos em Angular. Mas só devem ser utilizados para comunicação entre componentes através de serviços. Nos casos em que houver um '@Output()' a melhor opção ainda é EventEmitter. Os Subjects são muito úteis para implementar comunicação entre componentes cruzados e também é uma forma mais recomendada porque são mais eficientes do que as emissões de evento nos bastidores.
Eles devem ser importados do pacote 'rxjs', conforme abaixo:
import { Subject } from "rxjs";
Após isso, dentro do serviço o qual irá ser definido o Subject, basta criar uma propriedade na qual você possa atribuir uma instancia dele:
displayDashboardConverter = new Subject<>()
E definir no tipo genérico os dados que serão emitidos por ele:
displayDashboardConverter = new Subject<boolean>()
Neste exemplo, o Subject foi criado no serviço de moedas -> CoinService, pois irá emitir um dado do tipo booleano, após o clique em um botão, que será uma condição para renderizar o conversor de moedas.
No componente que você deseja emitir um dado, nesse caso, no botão; após injetar a classe do serviço na função construtora do componente conforme essa documentação sobre Serviços sugere, você pode chamar esse Subject, acessar o método next() e passar esse dado para o próximo componente, o qual pode ser qualquer outro componente da aplicação, acima ou abaixo na árvore de componentes:
<button id="show-converter-action" *ngIf="!conversorIsShowing" (click)="showConverter()">
<span class="material-symbols-outlined"> currency_exchange </span>
</button>
showConverter() {
this.coinService.displayDashboardConverter.next(true)
}
No exemplo de código acima, ao clicar no -> convert-action.component.html, é acionada uma função em seu arquivo .ts, a qual acessa o Subject instanciado no serviço de moedas e emite um dado booleano para qualquer outro componente da aplicação.
Já no componente que você deseja pegar/acessar esse dado emitido, você deve injetar o serviço no qual foi criado o Subject na construção do componente, e através do Subject acionar o subscribe():
constructor(private coinService: CoinService) { }
this.subjectSubscription = this.coinService.displayDashboardConverter.subscribe((data)=> {this.displayConverter = data})
Neste exemplo de código, na função inicializadora do -> conversion-dashboard.comoponent.ts, pegamos o valor booleano que o -> convert-action.component.ts passou/emitiu para o próximo componente na aplicação quando clicado, utilizanto o método next() do Subject; foi alterada uma propriedade do -> conversion-dashboard.comoponent.ts a qual é responsável por renderizar este elemento quando seu valor for true, que neste caso, corresponde ao valor emitido por -> convert-action.component.ts utilizando Subject.
constructor(private coinService: CoinService) { }
this.subjectSubscription = this.coinService.displayDashboardConverter.subscribe((data)=> {this.displayConverter '<--- propriedade responsável por renderizar o elemento' = data '<--- valor emitido do convert-action.component.ts utilizando Subject'})
1 - Foram feitas mais comunicações entre os componentes supracitados com a utilização do mesmo Subject, mas a nível de exemplificação, as que foram mostradas acima são suficientes.
2 - Subject é um tipo especial de observável. Nos observáveis também chamamos o método next(), mas de dentro deles quando o criamos ou quando comunicamos essa 'fase' do dado observado ao subscribe(). Já o Subject é mais ativo porque chamamos o next() passando um dado para o próximo componente que assinar/inscrever o dado observado e emitido por ele.
3 - Assim como os observáveis criados manualmente, é necessário cancelar o método subscribe()/o que ele está retornando, e isso deve ser feito dentro do método ngOnDestroy, que é executado no momento/ciclo de vida da destruição do componente.
Para isso, conforme essa documentação sobre Observáveis sugere, você deve armazenar a chamada do subscribe() em uma propriedade do tipo Subscription, importada do pacote rxjs:
import {Subscription } from 'rxjs';
subjectSubscription!: Subscription;
E no ciclo de vida de destruição do componente, dentro de ngOnDestroy(), cancelar o subscribe() a partir desta propriedade chamando unsubscribe():
ngOnDestroy() {
this.subjectSubscription.unsubscribe()
}
Isso evita vazamentos de memória ou qualquer coisa do tipo, evitando que qualquer dado observável ou execução de código derivada de observáveis fiquem em execução ao deixar o componente/rota, o que melhora o desempenho da aplicação.
Sinta-se a vontade para explorar todo seu conhecimento utilizando este projeto, qualquer dúvida ou sugestão me procure no Linkedin.