0

I have a component "checklist" and a component "checklist-item". The checklist component creates checklist-items via ng-for. I additionally have a menu component with a button, which is supposed to change the layout of my checklist items. I therefore have a message service with a subject, such that both can communicate with each other.

Checklist component (HTML):

<div class="checklist-wrapper">
    <div *ngFor="let listItem of listItems; let i = index">
        <app-checklist-item [item]="listItem"  [index]="i"></app-checklist-item>
    </div>
</div>

Message Service:

import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class MessageService {

  private gridOn$ = new Subject<boolean>();

  constructor() { }

  onSendGridOn(gridOn: boolean) {
    this.gridOn$.next(gridOn);
  }

  getGridOn(): Observable<boolean> {
    return this.gridOn$.asObservable();
  }
}

Menu Component (TS):

import { Component, OnInit } from '@angular/core';
import { MessageService } from '../services/message.service';

@Component({
  selector: 'app-menu',
  templateUrl: './menu.component.html',
  styleUrls: ['./menu.component.css']
})
export class MenuComponent implements OnInit {

  gridOn = false;

  constructor(private messageService: MessageService) { }

  ngOnInit() {
  }

  onToggleGridMode(){
    this.gridOn = !this.gridOn;
    this.messageService.onSendGridOn(this.gridOn);
  }
}

Checklist-item component (TS):

constructor(private messageService: MessageService) {
    this.subscription = this.messageService.getGridOn().subscribe( () => console.log("works"));
}

I also have a header component. In the header component, the subscription works fine. But for the checklist-item component and the checklist component, the subscription is not doing anything. If i use a Behavior Subject instead, it works once on initialization, but not afterwards.

Q.uestion
  • 183
  • 1
  • 11
  • Do you need the service? You could just set an input property as long as the parent is passing this value to the child. – cs95 Dec 19 '19 at 09:33
  • Yes I think I need a service, since the menu component is not the parent of checklist-item. – Q.uestion Dec 19 '19 at 09:37
  • I think that you are doing the `onSendGridOn` function before the `Checklist-item` component reach the constructor. Can you check this please? You can add a breakpoint in the onSendGridOn function and another one in the constructor of the component. If the onSendGridOn function is called before you break into the constructor, you have to load the component first and then do the call – Jacopo Sciampi Dec 19 '19 at 10:28
  • @JacopoSciampi The onSendGridOn function is first called, when I click the button. Therefore, the constructor is called first. – Q.uestion Dec 19 '19 at 10:50
  • Perfect. Then I would simply do `this.messageService.getGridOn().subscribe( () => console.log("works"))` just for a test purpose. If you need to close that subscription when the component gets destroyed you can use `pipe` along with `takeWhile` operator. – Jacopo Sciampi Dec 19 '19 at 10:52
  • @JacopoSciampi If I'm getting you right, you mean that I should not save the subscription to a variable? I already tried it, but it does not work either. – Q.uestion Dec 19 '19 at 12:33

2 Answers2

2

You should return this.gridOn$ as is and subscribe to it instead of using .asObservable() which basically used to prevent the stream source from being publicly available in every component and that's the opposite of what you want.

getGridOn(): Observable<boolean> {
    return this.gridOn$;
}

To know more about when to use asObservalbe() check this thread.

MEDZ
  • 2,227
  • 2
  • 14
  • 18
  • 1
    Don't return the subject directly, that's bad practice. Keep a reference to the asObservable() and return that. – cs95 Dec 19 '19 at 09:19
  • @MEDZ My button should somehow work like a toggle. So it should switch the layout. Without an observable, I think I can't achieve that? – Q.uestion Dec 19 '19 at 09:30
  • @cs95 Thats also what I thought. – Q.uestion Dec 19 '19 at 09:31
  • @Q.uestion If you want to use it in the header only, then using asObservable() is the logical option. If you want the menu toggle to be available across all components then it should be removed. – MEDZ Dec 19 '19 at 09:46
  • @cs95 I understand your concern but both menu component and checklist components are not related, I imagine even that they are not under the same module. – MEDZ Dec 19 '19 at 09:46
  • @MEDZ It should be available only to the checklist-item component, but to all instances of it (ng-for). And all my components are in the same module, since so far it is only 6 components. – Q.uestion Dec 19 '19 at 09:49
0

The problem seems to be with Visual Studio Code. When I added my MessageService to my component, the auto import imported it as

import { MessageService } from '../message.service.js';

With .js at the end, it won't work.

Q.uestion
  • 183
  • 1
  • 11