Skip to content

Latest commit

 

History

History
161 lines (120 loc) · 9.49 KB

README.md

File metadata and controls

161 lines (120 loc) · 9.49 KB

Angular/RXJS - Operadores e Subjects

This project was generated with Angular CLI version 15.1.6.

Logo

Sumário

Sobre Operadores | Como utilizar Operadores | Observações de Operadores | Sobre Subjects | Como utilizar Subjects | Observações de Subjects | Conclusão

Sobre Operadores

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.

Como utilizar Operadores

É 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.

Observações de Operadores

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.

Sobre Subjects

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.

Como utilizar Subjects

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'})

Observações de Subjects

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.

API's utilizadas

Tecnologias

Conclusão

Sinta-se a vontade para explorar todo seu conhecimento utilizando este projeto, qualquer dúvida ou sugestão me procure no Linkedin.