2

EDIT 2: This appears to be my general problem, and solution (using setTimeout so Angular's lifecycle can happen). I'll either close this or post an answer to my own question when I can.

See EDIT for a simpler repro that doesn't involve Subjects/Observables but is essentially the same problem.

I have a parent component that's responsible for fetching data from a service.

export class ParentComponent implements OnInit {
  public mySubject: Subject<Foo[]> = new Subject<Foo[]>();
  public buttonClicked = false;
  private currentValues: Foo[] = null;

  constructor(private SomeService myService) { }

  this.myService.get().subscribe(values => {
    this.mySubject.next(values); // Does NOT work when a child component is hidden, as expected.
    this.currentValues = values; // Keep value so we can manually fire later.
  });

  private buttonClickHandler() {
    this.buttonClicked = true;
    this.mySubject.next(this.currentValues);
  }
}

This data is subscribed to in the HTML by a child component. This component is hidden by default via *ngIf, and only becomes visible on a button click:

<app-child-component [values]="mySubject.asObservable()" *ngif="buttonClicked" />

In the parent component above you see I'm trying to pass the current available data to the child by invoking next() when the component is made visible in some way:

this.mySubject.next(this.currentValues);

This does not work when initially un-hiding the component via *ngIf. If I click the button a second time, which then calls next() again, then it works as expected. But when Angular is in the current context of un-hiding something, observables aren't getting their data. (This also happens when things are unhidden by other means, but the result is the same: If in the same method, the subject/data passing does not work; the component has to already be visible as of the method call.)

I'm guessing the binding to the observable is not happening until after *ngIf shows the child component, after the method call resolves. Is there some place I can hook into that I can then pass child data down?

EDIT for clarification: I don't believe this is an issue of Subject vs. BehaviorSubject. I'm not having issue passing the data. The issue is that the data-passing (confirmed via console.log()) is not occurring at all in the first place. It's not that the child component is receiving a null value. The subscription just isn't firing to the child.

I found I can reproduce this in a simpler fashion too: Trying to select an element in the DOM of *ngIf HTML reveals undefined if I make *ngIf's value true within the same Angular method.

<div *ngIf="buttonClicked">
  <div id="someElement">Foo</div>
</div>
public someMethod(): void {
  this.buttonClicked = true;
  const container = document.getElementById('someElement'); // DOES NOT WORK if this.buttonClicked was false at the start of this method!
}
Doctor Blue
  • 3,769
  • 6
  • 40
  • 63

1 Answers1

0

You going to need to use a BehaviourSubject instead of Subject, which emits the previously set value initially.

What is the difference between Subject and BehaviorSubject?

Rossco
  • 3,563
  • 3
  • 24
  • 37
  • I don't think this is a solution to my problem. I'm not having issue holding onto a current value (I can refactor my code later to use `BehaviorSubject` instead of holding onto the value manually). My issue is that the emission is not happening at all to a component who was just un-hidden. I don't think `BehaviorSubject` would fix that? Could be wrong. – Doctor Blue Jan 27 '20 at 13:56