Question
What is the most elegant way to get @ViewChild after corresponding element in template was shown? Below is an example. Also Plunker available.
Template:
<div id="layout" *ngIf="display">
<div #contentPlaceholder></div>
</div>
Component:
export class AppComponent {
display = false;
@ViewChild('contentPlaceholder', {read: ViewContainerRef}) viewContainerRef;
show() {
this.display = true;
console.log(this.viewContainerRef); // undefined
setTimeout(()=> {
console.log(this.viewContainerRef); // OK
}, 1);
}
}
I have a component with its contents hidden by default. When someone calls show()
method it becomes visible. However, before angular 2 change detection completes, I can not reference to viewContainerRef
. I usually wrap all required actions into setTimeout(()=>{},1)
as shown above. Is there a more correct way?
I know there is an option with ngAfterViewChecked
, but it causes too much useless calls.
A simplified version, I had a similar issue to this when using the Google Maps JS SDK.
My solution was to extract the
div
andViewChild
into it's own child component which when used in the parent component was able to be hid/displayed using an*ngIf
.Before
HomePageComponent
TemplateHomePageComponent
ComponentAfter
MapComponent
TemplateMapComponent
ComponentHomePageComponent
TemplateHomePageComponent
ComponentMy goal was to avoid any hacky methods that assume something (e.g. setTimeout) and I ended up implementing the accepted solution with a bit of RxJS flavour on top:
My scenario: I wanted to fire an action on a
@ViewChild
element depending on the routerqueryParams
. Due to a wrapping*ngIf
being false until the HTTP request returns the data, the initialization of the@ViewChild
element happens with a delay.How does it work:
combineLatest
emits a value for the first time only when each of the provided Observables emit the first value since the momentcombineLatest
was subscribed to. My SubjecttabSetInitialized
emits a value when the@ViewChild
element is being set. Therewith, I delay the execution of the code undersubscribe
until the*ngIf
turns positive and the@ViewChild
gets initialized.Of course don't forget to unsubscribe on ngOnDestroy, I do it using the
ngUnsubscribe
Subject:An alternative to overcome this is running the change detector manually.
You first inject the
ChangeDetectorRef
:Then you call it after updating the variable that controls the *ngIf
The answers above did not work for me because in my project, the ngIf is on an input element. I needed access to the nativeElement attribute in order to focus on the input when ngIf is true. There seems to be no nativeElement attribute on ViewContainerRef. Here is what I did (following @ViewChild documentation):
I used setTimeout before focusing because the ViewChild takes a sec to be assigned. Otherwise it would be undefined.
The accepted answer using a QueryList did not work for me. However what did work was using a setter for the ViewChild:
The setter is called once *ngIf becomes true.
This could work but I don't know if it's convenient for your case: