0

I am new to rxjs, so I would like to ask a question regarding Angular 2, Observables and BehaviorSubject/Subject.

So, what I want to achieve is : onclick of a button inside a ComponentA to notify other components for example ComponentB, ComponentC.

What I did so far is to create a service:

private isMenuOpenBS = new BehaviorSubject(false);

  toggleMenu(): void {
    this.isMenuOpenBS.next(!this.isMenuOpenBS.getValue());
  }

  getMenuState(): Observable<boolean> {
    return this.isMenuOpenBS.asObservable();
  }

Then having one component with provide menuService is calling the this.menuService.toggleMenu() which changes the value of the BehaviorSubject. Code :

toggleMenu(): void {
    this.menuService.toggleMenu();
    this.menuService.isMenuOpenBS.subscribe(
      (data) => {
        console.log(data)
      },
      (e) => {console.log(e)},
      () => {console.log('completed')}
    )
  }

And another component that OnInit() subscribes to the getMenuState() which is an Observable, but it get's values only on OnInit(). Code:

ngOnInit(): void {
    this.menuService.getMenuState().subscribe(
      (data)=>{
        this.messages = data;
        console.log('nav component');
      },
      (e) => {console.log(e)},
      () => {console.log('completed')}
    )
  }

The complete function is never called.

Any ideas what am I doing wrong or if i am missing something in the logic ?

--

Edit : So, to explain a bit more the problem was that I could see the first value that the observable had oninit but nothing else. No error no complete after that or no "next" value which was wrong since I was sending new values from the subject. In the end the problem was with the provider list of the components and not a problem of observables or subjects, but before solving the problem it wasn't easy to see that the problem was there.

DrNio
  • 1,936
  • 1
  • 19
  • 25
  • Where do you "complete" the observable? – eko May 30 '17 at 18:17
  • Nowhere. Is that what is missing ? In my mind, I am thinking that I don't want to `complete` it so it can get values all the time. Or I have to `complete` it every time it gets the value, so it can get the new one after the other component will call that `toggle` method ? – DrNio May 30 '17 at 18:25
  • That's what you are missing. If you complete the subject the observable dies. – eko May 30 '17 at 18:29
  • So i have to call `complete()` somewhere ? Sorry, I am a bit confused, is it possible to write where in the code should I do it ? Basically, I have 1 `shared service` and 2 `components`. – DrNio May 30 '17 at 18:33
  • Why do you want to complete the observable? You won't be able to `next` values if you do so – eko May 30 '17 at 18:33
  • The only code I use is the one I pasted and I removed the last callbacks `() => {console.log('completed')}` from everywhere, but still the same : the `.subscribe` is printing only once the `nav component` (`OnInit`) – DrNio May 30 '17 at 18:37
  • Possible duplicate of [Angular - shared service between components doesn't work](https://stackoverflow.com/questions/43997489/angular-shared-service-between-components-doesnt-work) – eko May 31 '17 at 04:44
  • Well, in the end the solution was regarding the providers array. But at the beginning the whole problem seemed to be the observable was not getting next values. After discussing about it and did some research I saw that the "reactive" code didn't have any issue and that the problem was in the providers array. – DrNio Jun 05 '17 at 23:16
  • If you also think it's a duplicate, can you mark it as so please? – eko Jun 06 '17 at 04:34

3 Answers3

0

You can communicate between the components with child parent relationship or through the service.

https://angular.io/docs/ts/latest/cookbook/component-communication.html

But there is a way through ngrx/store. This concept is similar to Redux. you can try it.

Praneeth Reddy
  • 424
  • 3
  • 7
  • Thank you but I didn't want to communicate parent-child or child-parent. Rather than one service holds the state and shares it to multiple components without introducing another library. Thank you for the answer though. This http://jasonwatmore.com/post/2016/12/01/angular-2-communicating-between-components-with-observable-subject helped me realise the issue with the `providers` array. – DrNio May 30 '17 at 19:21
0

There was nothing wrong with the code I pasted. My issue was that the 2 components that I created had injected 'MenuService' in their providers array, so I didn't have singleton.

I removed the service from the providers array from both components and it worked!

PS: This http://jasonwatmore.com/post/2016/12/01/angular-2-communicating-between-components-with-observable-subject was a useful tutorial to realise the issue that I had with the providers array.

DrNio
  • 1,936
  • 1
  • 19
  • 25
0

A few things...

  1. You don't need to return your BehaviorSubject as an observable, you should be able to subscribe to observables no issue.
  2. You shouldn't keep on subscribing to the service every time the toggle function gets called. You're creating a lot of subscriptions by doing so. If you took my advice from above you can just log the return of the getValue() function.

Glad you resolved it though!

MichaelSolati
  • 2,847
  • 1
  • 17
  • 29
  • Hi @MichaelSolati. Regarding your first comment I believe you find it more often to return an observable instead of the subject/behavior subject for immutability. So that any component won't alter the state - only get it. About the second comment I could you please elaborate a little bit more about how many subscriptions are created ? From my understanding the only subscription that exist are the ones that the component will `.subscribe`. The `console.log()` in the code was just for the PoC. – DrNio Jun 07 '17 at 04:48
  • Well it's kind of like the `setInterval()` function in which you can call on the function repeatedly. And each instance is a separate occurrence of the subscription. Because it's not attached to a variable in the component you never end the subscriptions/`unsubscribe()` (which a new subscription is initialized everytime `toggleMenu()` is called). – MichaelSolati Jun 07 '17 at 04:53
  • Okey, I see your point. Yes, it should be something like : `this.subscription = this.menuService.getMenuState().subscribe( (data) => { this.menuState = data; }, (e) => { console.log(e); }, () => {console.log('completed'); }); ` With a `private subscription: Subscription`. But, as I said before the `console.log` was for the PoC. Thanks for the comments though! – DrNio Jun 07 '17 at 05:04