Search code examples
angularinheritancebehaviorsubjectasync-pipe

Angular extend component with BehaviorSubject async pipe not working


I can't seem to trigger the parent component's async pipe from a child component to update the view. Which might sound a bit confusing but let me try to explain by showing a simplified version of my code.

If have got a class that is extended by another class.

export class ParentComponent implements OnInit{
    protected subject$: BehaviorSubject<string[]>;

    ngOnInit() {
        this.subject$ = new BehaviorSubject<string[]>([]);
    }
}

With a template that looks like:

<span *ngFor="let niceString of subject$ | async">
    {{niceString}}
</span>

Extended by:

export class ChildComponent extends ParentComponent implements OnInit{   
    ngOnInit() {
        super.ngOnInit();

        this.subject$.next(['string1', 'string2']);
    }
}

The template is not updated in the browser. If I would call the line with subject$.next in the parent component it would update as expected and show the two strings in the browser.

So I tried to play around with it by calling SetTimeOut with 3 seconds delay to see if somehow it was called to fast. This didn't solve the issue. I also add the subject$.next in a function in the parent that I then called from the child and also this wouldnt solve it. But if I would call the same new function in the parent class the view was updated as expected.

I also added a custom subscription in the parent that listens to the subject$ like so:

protected subjectSubscription: Subscription;

this.subjectSubscription = this.subject$.pipe(take(2)).subscribe((justChecking: string[]) => {
    debugger;
});

Which will be triggered as expected but the view in the browser isnt updated with the values 'string1', 'string2'.

Am I missing something here?

(EDIT) Add StackBlitz: https://stackblitz.com/edit/angular-tt65ig


Solution

  • You're mixing inheritance and composition:

    • The child component is a parent component, because it extends Parent
    • The child component has a parent component, because the template of the child contains <app-parent>.

    So you're creating two instances of the parent component:

    • one by using <app-child> in the root component template. This instance of parent is an instance of child. Its subject emits two strings, but its template, which only has <app-parent> never displays these two strings.
    • one by using <app-parent>in the child component template. This instance of parent is not an instance of child. Its template displays the content of its subject, but its subject never emits anything, since it's not an instance of the child component, and only the child component contains the code to emit 2 strings.

        parent
         - has subject1
          ^
          -
          |
          |
       extends
          | 
          |                          displays
        child --------------------------------> parent
         - emits from subject1                   - has subject2
                                                 - displays content of subject2
      

    How to fix? Don't do that. Either use inheritance, or, preferrably, use composition, but don't mix the two.