2
votes

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

3
Can't reproduce: stackblitz.com/edit/angular-e8eryq. Post a complete minimal example reproducing the issue as a stackblitz, as I just did.JB Nizet
Ok, i will. Thanks for the suggestion.Robin

3 Answers

3
votes

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.

1
votes

I can't reproduce the described behavior. Have a working stackblitz here: https://stackblitz.com/edit/angular-gfxqpe

Do you maybe expect the parent's template to be used by the child? That won't work. You could reference the same html file like

@Component({
  selector: 'app-child',
  templateUrl: '../parent/parent.component.html',
  styleUrls: ['./child.component.css']
})
-1
votes

Lifecycle methods are not inherited therefore rendering your ChildComponent's ngOnInit() method call useless.

Though I would much prefer you use a service design pattern, to do this the way you have things you could use super.ngOnInit().