For some reason my @ViewChild in my Angular 5 App does not work.
I have defined it like this in my component:
case-detail.component.html:
<div class="inner-tab-content" #innerTabContent>
<!-- // more content here -->
</div>
I have implemented @ViewChild
in my controller (above the constructor) like this:
case-detail.component.ts
@ViewChild('innerTabContent') tabContentElement: ElementRef;
And I want to access it here in the component:
case-detail.component.ts
ngAfterViewInit() {
console.log("scroll top: " + this.tabContentElement.nativeElement);
}
I've implemented the AfterViewInit
interface. ngAfterViewInit() is called correctly. However, this.tabContentElement
is always undefined
.
Any help is greatly appreciated :)
ViewChild()
works fine on latest plunker Angular version with the scenario you describe.
Demonstration in this plunker : https://plnkr.co
/edit/KzWnkE5Hvp7NUow6YAxy
EDIT: Here is a replacement StackBlitz for the above Plunker: https://stackblitz.com/edit/angular-ivy-pzaglm
component :
ngAfterViewInit() {
console.log(this.testView); // correctly outputs the element in console, not undefined
}
Check that ElementRef and ViewChild are correctly imported from '@angular/core'
Your element might simply not be there at the time of AfterViewInit (in case there is a *ngIf
, for instance. (seems the case as per your comments)
In the latter case, you can use a wrapper element and ViewChildren
, that emits some event when a new child element is added - more info on documentation here : https://angular.io/api/core/ViewChildren
note that there might be some issue with native div
as per this question : @ViewChildren does not get updated with dynamically added DOM elements , but this can be worked around by using a new component that wraps your div, for instance.
EDIT
Or you can also use a timeout to wait for the component to be rendered. I must say that I find this solution 'dirty', but glad it works for you :)
However, even if you access to the child component in the AfterViewInit
, sometimes the @ViewChild
was still returning null
. The problem can be caused by the *ngIf
or other directive.
The solution is to use the @ViewChildren
instead of @ViewChild
and subscribe the changes
subscription that is executed when the component is ready.
For example, if in the parent component ParentComponent
you want to access the child component MyComponent
.
import { Component, ViewChildren, AfterViewInit, QueryList } from '@angular/core';
import { MyComponent } from './mycomponent.component';
export class ParentComponent implements AfterViewInit
{
//other code emitted for clarity
@ViewChildren(MyComponent) childrenComponent: QueryList<MyComponent>;
public ngAfterViewInit(): void
{
this.childrenComponent.changes.subscribe((comps: QueryList<MyComponent>) =>
{
// Now you can access to the child component
});
}
}
To get this to work consistently in my case, I replaced all occurrences of
*ngIf="check"
with
[style.display]="check ? 'block' : 'none'"
Otherwise the ViewChild components that didn't exist when my view first loaded would potentially remain undefined.
A different answer to this question would be sometimes, The child component is still not rendered, when you access it. For me it was due to an *ngIf so that child component is still not there. For an example,
<div *ngIf="check">
<app-child-item></app-child-item>
</div>
And in your parent .ts file you try to access childItem while check is set to false.