BehaviorSubject para comunicação entre componentes
Tags: angular, rxjs
Published: 2019-07-05 17:34:49

As maneira mais simples de trafegar dados entre componentes é utilizando *inbound property* (@Input) e **EventEmitter** (@Output). Muitas vezes isso é suficiente, porém, podemos passar por alguns problemas:
* E se quiséssemos alterar o dado da variável que estamos passando via *inbound property*? Teríamos que renderizar o componente novamente? 
* E se quiséssemos executar alguma lógica quando detectarmos alteração no valor?
* E se o componente filho alterar esse dado? A única alternativa é criar um "caminho de volta" para avisar o componente pai?
* Será que existe uma forma de manter uma fonte centralizada desse dado caso vários componentes precisem dele?

Uma das alternativas que temos para solucionar esses problemas é fazendo uso do **BehaviorSubject** ao invés de passar propriedades comuns para outros componentes. Seu funcionamento lembra o **EventEmitter**, mas trata-se de um **Observable** que sempre será instanciado e inicializado com um valor. Poderemos passar este **BehaviorSubject** para outros componentes via *inbound property* ou centralizá-lo em um serviço responsável pela comunicação, dessa forma, vários componentes poderão se inscrever(subscribe) nessa fonte de dados e manterem-se atualizados do seu último valor.

---

###Resumo
Em um componente pai ou serviço, devemos criar e instanciar um **BehaviorSubject** que será disponibilizado para outros componentes ou serviços. Novos valores são enviados ao **BehaviorSubject** através do método **next()**. Com o **subscribe()**, podemos detectar quando houve alterações. Podemos obter o valor atual durante o próprio **subscribe()** e passá-lo a alguma variável ou acessando a propriedade **value** do **BehaviorSubject**: *nomeBehaviorSubject.value*. Também podemos exibir o valor no template html utilizando a **pipe async**: *nomeBehaviorSubject | async*.

---

###Na prática
Explicarei sucintamente seu uso e no final disponibilizarei um exemplo funcional, como de praxe. Além do componente principal, terei outros dois componentes, um para emitir valores e outro para exibi-los:

![](https://thepracticaldev.s3.amazonaws.com/i/w392ja32fureofvs4r3k.PNG)

---

####BehaviorSubject via inbound property
O **app.component** é o nosso "componente pai", que será responsável por criar a instancia do **BehaviorSubject** que será passada para os componentes filhos:
```typescript
export class AppComponent {
  nomeBehaviorSubjectPai= new BehaviorSubject<string>("Felipe");
}
```

Teremos então 2 componentes filhos, **emissor.component** e **visualizador.component**. Ambos receberão o **nomeBehaviorSubjectPai** via *inbound property*.
Definimos um Input para ambos. Não há necessidade de instanciar o **BehaviorSubject** como feito no **app.component**:
```typescript
@Input() nomeBehaviorSubject: BehaviorSubject<string>;
```

```html
<div class="row">
  <div class="col-6 border">
    <app-emissor class="m-1" [nomeBehaviorSubject]="nomeBehaviorSubjectPai"></app-emissor>
  </div>
  <div class="col-6 border">
    <app-visualizador class="m-1" [nomeBehaviorSubject]="nomeBehaviorSubjectPai"></app-visualizador>
  </div>
</div>
```

Em **emissor.component**, mandaremos um novo valor ao **nomeBehaviorSubject** através do método **next()**, para isso, criei o método abaixo, que será acionado pelo template:
```typescript
  enviarViaInput(nome: string) {
    this.nomeBehaviorSubject.next(nome);
  }
```

Dessa forma já conseguimos acessar o valor diretamente pelo **BehaviorSubject** recebido pelo nosso **visualizador.component**. Fiz isso em seu template, conforme código abaixo:
*Obs.: não é a única maneira de exibir o valor*
```html
<div class="col-12">
  Valor: {{nomeBehaviorSubject | async}}
  <br>
  Recebido {{nomeCount}} vez(es).
</div>
```

Observe que também estou exibindo o **nomeCount**. Também podemos nos inscrever ao **nomeBehaviorSubject**, receber o valor emitido e executar alguma lógica. No caso, apenas estou contando a quantidade de valores que foram recebidos:
```typescript
  ngOnInit() {
    this.nomeBehaviorSubjectSubscription = this.nomeBehaviorSubject.subscribe(valor => {
      this.nomeCount++;
    });
  }
```

---

####BehaviorSubject via service

Seu uso será semelhante ao exemplo anterior, porém com um service criando a instancia do **BehaviorSubject**, no caso, o **app.service**, com alguns métodos para passar um novo valor e obter o **BehaviorSubject**:
```typescript
export class AppService {
  private musicaServiceBehaviorSubject = new BehaviorSubject<string>(`Dio - Egypt`);

  constructor() { }

  alterarMusica(valor: string) {
    this.musicaServiceBehaviorSubject.next(valor);
  }

  obterMusica() {
    return this.musicaServiceBehaviorSubject;
  }
}
```

O **emissor.component** receberá o service por injeção e poderá passar um novo valor através do método **alterarMusica()**
```typescript
constructor(private appService: AppService) { }

enviarViaService(musica: string) {
  this.appService.alterarMusica(musica);
}
```

Da mesma forma, o **visualizador.component** também recebe o service por injeção e faz uso do **subscribe** do **BehaviorSubject** para executar uma lógica:
```typescript
ngOnInit() {  
  this.musicaSubscription = this.appService.obterMusica()
    .subscribe(valor => {
      this.musicaCount++;
    });
}
```

Também criei um método **get musica()** para acessar o valor do template. O get nos permitirá acessar **musica** como se fosse uma variável comum de nosso componente, conforme demonstrado:
```typescript
get musica(): string {
  return this.appService.obterMusica().value;
}
```

```html
<div class="row">
	<div class="col-12">
		<h4>Via service</h4>
	</div>
	<div class="col-12">
		Valor: {{musica}}
		<br>
		Recebido {{musicaCount}} vez(es).
	</div>
</div>
```

--- 

####Vamos ver funcionando?
*Recomendo abrir em uma nova aba.*
{% stackblitz angular-comunicacao-behaviorsubject %} 
Caso queira abrir em nova aba ou não tenha conseguido visualizar o embedded, clique [aqui](https://stackblitz.com/edit/angular-comunicacao-behaviorsubject).

---

Observe que fiz questão de dar um **unsubscribe()** nos **BehaviorSubject**s. Caso não tenha visto meu post anterior, nele demonstro a importância disso:
[Observables: cancelar inscrição (unsubscribe) é importante!](https://dev.to/felipedsc/observables-cancelar-inscricao-unsubscribe-e-importante-51bc)