Angular 6 nested ViewChild inside ng-template is null
Solution 1
You can read the child component without the refrence variable like this
@ViewChild(AudioComponent)
audio: AudioComponent;
This will give you the instance of the child component - where you can access the method
this.audio.someComponentFunction()
Your html
<ng-template #modal let-modal>
<app-audio></app-audio>
</ng-template>
This will solve your issue i think - Happy coding
Update:
Hope i found a workaround for this issue - if in case you want to trigger only one function you can use this method
I have just added a property with getter
and setter
and triggered the function when we set
the value
@Input()
get triggerFunction(): boolean {
return this.runFuntion;
}
set triggerFunction(value: boolean) {
this.runFuntion = value;
this.someFunction();
}
So this causes to trigger the function every time when the model show up - property mentioned above belongs to the child component which is nested inside the <ng-template>
so finally the model template will read as mentioned below:
<ng-template #modal let-modal>
<app-audio [triggerFunction]="true"></app-audio>
</ng-template>
Hope this will act a workaround for now - Thanks
Solution 2
You can call the method audio.someFunction()
from the template itself.
<ng-template #modal let-modal>
<div style="background-color: red;">
<h1>Modal header</h1>
<app-audio #audio></app-audio>
<!-- on click, call audio comp method someFunction() using its reference -->
<button (click)="audio.someFunction()">Operate with audio from inside modal</button>
</div>
</ng-template>
No need of @ViewChild
property here. This should do the trick for you.
Forked demo
Solution 3
For me all this solutions did not work and I still wanted to access my own component inside a third party ng-template. Here is my 'solution'. I don't think this is best practice but a desperate solution to get what I want ;-) It only works for your own components of course.
// mycomponent.ts => component that needs to be accessed
import { Component, Output, EventEmitter, AfterViewInit } from '@angular/core';
@Component({
selector: 'my-component',
templateUrl: './mycomponent.html'
})
export class MyComponent implements AfterViewInit {
@Output() initialized: EventEmitter<MyComponent> = new EventEmitter<MyComponent>();
ngAfterViewInit(): void {
this.initialized.emit(this);
}
reload(): void {
// Do something
}
}
// somecomponent.html => component with <ng-template> holding MyComponent
<ng-template>
<div class="btn-group ml-2">
<my-component (initialized)="onMyComponentInitialized($event)"></my-component>
</div>
</ng-template>
// somecomponent.ts => component with <ng-template> holding MyComponent
import { Component, OnDestroy } from '@angular/core';
import { MyComponent } from '../../my-component';
@Component({
selector: 'some-component',
templateUrl: './some-component.html'
})
export class SomeComponent implements OnDestroy {
private _myComponent: MyComponent = null;
onMyComponentInitialized(component: MyComponent) {
this._myComponent = component;
}
someOtherMethod() {
if (this._myComponent) {
// Call some method on the component
this._myComponent.reload();
}
}
ngOnDestroy() {
this._myComponent = null;
}
}
Related videos on Youtube
Comments
-
Erwol almost 2 years
We are using a modal (ng-bootstrap's one) in our application. That modal looks like:
<ng-template #modal let-modal> <app-audio #audio></app-audio> </ng-template>
And it's logic:
@ViewChild('modal') modal: ElementRef; @ViewChild('audio') audio: AudioComponent;
The modal is opened with:
this.modalService.open(this.modal, { size: 'lg' });
Everything fine till here. The modal opens and the audio component is shown. But now, we want to access the logic that is inside the component, and when doing something like:
this.audio.somePublicComponentFunction()
It happens that this.audio is null. I have already tried to get the child with angular's change detector, but cannot find a way to properly link this.audio with the actual component. Any ideas? Thanks a lot.
You can see the issue here: stackblitz.com/edit/angular-ofmpju
-
Amit Chigadani over 5 yearsThis could be a problem when there are multiple
app-audio
tags in a template. That is the reason we have template reference. And coming to your solution, how does that differ from using template reference? Aren't they both same? -
rrd over 5 yearsCan you use ViewChildren() in that case then @AmitChigadani ?
-
Erwol over 5 yearsHello Rahul, and thanks for your idea. Unfortunately it's still not working. I think the problem comes because the modal content is not rendered till the modal itself is opened. And as ViewChild properties are set before ngAfterViewInit, and our modal is opened manually by the user, for some reason change detector doesn't get the changes here and this.audio is still null.
-
Admin over 5 years@AmitChigadani
@ViewChild('audio', { observe: AudioComponent })
and no, they aren't the same, view children inspect the HTML tag by default. -
Rahul over 5 years@AmitChigadani template reference will not be available when the page loads - since it has been inside the
<ng-template>
so when you read the component directly that might work - seems to be an idea not sure about it - it worked for me in one scenario -
Amit Chigadani over 5 years@trichetriche He is still using
@ViewChild
with component name and notViewChildren
which is same as using@ViewChild
with template reference -
Admin over 5 years@AmitChigadani
@ViewChild('audio', { observe: AudioComponent })
uses the name of the template variable, so no, you don't use the component name anymore. You use the class to explicitly state what to observe. -
Amit Chigadani over 5 yearsAgreed, but in the answer above, template reference is not being used. That is what I am pointing out here.
-
Admin over 5 yearsWell that's beside the point since the OP doesn't have several instances, and I have been answering you on how to be able to use the variable name anyway.
-
Erwol over 5 yearsHi @RahulSwamynathan, I updated my question with a code example, in case you want to check it out. Thanks.
-
Rahul over 5 years@Erwol I have updated the ans with some changes - Please have a look
-
Erwol over 5 yearsThanks, this is what helped me to figure out the final solution. I created a method for the modal component called setAudioComponent, that is called by the user by interacting with the modal. This method receives the #audio component and sets the private property.
-
Geo242 about 2 yearsIn certain situations I think this is a perfectly reasonable approach. Especially when the content within the template is not rendered when the host component is initially instantiated. This is a more reactive approach.