How to init a component only when its tag is at `*ngIf=true`?
Asked Answered
M

2

13

Update:
Günter Zöchbauer provided a very acceptable answer that works perfectly (and thank you!). But I still have a question to check if what I am doing is the correct way of getting the result I seek. Context is given below.
What I expect with my custom tag on the parent view, is that when the *ngIf condition is false, the component is not loaded at all (or maybe most part of it). Is it normal that even if it is not in the DOM (because the condition is false), it is still loaded? Is there a better way to embed components' templates (and the logic of their class) into a parent component's template?

Original post

I am new to Angular 2 and so far I'm having much fun developing a small song track metadata application based on the Spotify API.

TL;DR: How to init a component only when its corresponding HTML tag (defined with the selector property of @Component) if *ngIf="true"?


But of course I'm facing difficulties! I have this component, TrackDetailComponent, in the template of which I would like to include 2 other components. On clicking on a button, it would switch from one inner component to the other.
Here is how the template looks like (note that I only have developed one of the two inner components for now, so I used a placeholder in stead of the second one):

<div id="view">
    <tig-track-info *ngIf="childView == TrackDetailView.InfoView" [track]="track"></tig-track-info>
    <p *ngIf="childView == TrackDetailView.LyricsView">Here go the lyrics</p>
</div>

As you can see, my inner component (named TrackInfoComponent) has an input parameter. Here is an excerpt of that component:

@Component({
    selector: "tig-track-info",
    templateUrl: "app/components/track-info/track-info.component.html",
    styleUrls: ["app/components/track-info/track-info.component.css",
                "app/shared/animations.css"]
})
export class TrackInfoComponent implements OnInit {
    private _trackId: string = null;
    @Input() 
    track: Track = null;
    previewAudio: any = null; // The Audio object is not yet defined in TypeScript
    albumArtworkLoaded: boolean = false;
    ...
}

My real problem is not embedding the TrackInfoComponent inside the other one, but it is to sync them. The TrackDetailComponent calls an API asynchronously and then initializes the track property. But TrackInfoComponent also needs that track to be initialized in order to do its job or else I get a null reference exception (which is logical).

I tried setting the child component data in the ngOnInit() and setting the *ngIf of its parent's template to true only when the track is ready but it seems that ngOnInit() is called right away, even if the DOM element tig-track-info is not display with *ngIf.

I guess I could use events or my own init method in the child component, to call only when I'm ready, but I would like to know if there is a better way to do it using the Angular 2 component lifecycle hooks or any other method recommanded for that. Does anyone have any idea about that?

Thank you :-)

Mn answered 6/7, 2016 at 17:32 Comment(2)
Have you tried ngAfterViewInit or ngAfterViewChecked?Savoy
It doesn't work. I also tried AfterViewInit.Mn
P
13

You can use OnChanges which is called whenever an @Input() property value is changed by a template binding `[track]="..."

  export class TrackInfoComponent implements OnChanges {
    ngOnChanges(changes: SimpleChanges) {
      if(this.track) {
        ...
      }
    }
  }
Paramorph answered 6/7, 2016 at 19:45 Comment(6)
Perfect! So would that be the recommanded way to achieve what I want to do? I updated my original post to elaborate on this question. (I will mark your post as answer when my extra question will be answered :-) )Mn
ngIf adds/removes the element completely. If the condition becomes false the component is removed and destroyed. When it becomes true a new component instance is created and added. Is this what you want to know? When you don't want thus behavior use [hidden]="someExpression" to only show/hide instrad of add/removeNomarch
I know this behavior and that's why I don't understand why the child component is instanciated although the *ngIfCondition is false at first.Mn
I'm sure if the ngIf condition doesn't become true at any point, the component will not be instantiated at all. Maybe it's instantiated by other means. Can you provide a Plunker to reproduce (angular.io/resources/live-examples/quickstart/ts/plnkr.html)?Nomarch
Unfortunately, I changed my mind about that part, so I my UI differently. I don't have the code anymore. So let's close this topic. Thank you for your time and help :-)Mn
Thanks, that one pushed me over the 100k hurdle :-)Nomarch
M
0

1) first step

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';

Add ChangeDetectorRef in your code

2) second step

constructor(private changeDetector : ChangeDetectorRef){
}

3) whenever you there is change in your *ngIf="condition" ie condition becomes true, then you can call

  if(true==condition){
     this.changeDetector.detectChanges();
  }

4) This will render your html template.

You can try this,if it works for you.

Mckeown answered 28/12, 2018 at 11:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.