Angular 2 @ViewChild annotation returns undefined
I had a similar issue and thought I'd post in case someone else made the same mistake. First, one thing to consider is AfterViewInit
; you need to wait for the view to be initialized before you can access your @ViewChild
. However, my @ViewChild
was still returning null. The problem was my *ngIf
. The *ngIf
directive was killing my controls component so I couldn't reference it.
import {Component, ViewChild, OnInit, AfterViewInit} from 'angular2/core';import {ControlsComponent} from './controls/controls.component';import {SlideshowComponent} from './slideshow/slideshow.component';@Component({ selector: 'app', template: ` <controls *ngIf="controlsOn"></controls> <slideshow (mousemove)="onMouseMove()"></slideshow> `, directives: [SlideshowComponent, ControlsComponent]})export class AppComponent { @ViewChild(ControlsComponent) controls:ControlsComponent; controlsOn:boolean = false; ngOnInit() { console.log('on init', this.controls); // this returns undefined } ngAfterViewInit() { console.log('on after view init', this.controls); // this returns null } onMouseMove(event) { this.controls.show(); // throws an error because controls is null }}
Hope that helps.
EDIT
As mentioned by @Ashg below, a solution is to use @ViewChildren
instead of @ViewChild
.
The issue as previously mentioned is the ngIf
which is causing the view to be undefined. The answer is to use ViewChildren
instead of ViewChild
. I had similar issue where I didn't want a grid to be shown until all the reference data had been loaded.
html:
<section class="well" *ngIf="LookupData != null"> <h4 class="ra-well-title">Results</h4> <kendo-grid #searchGrid> </kendo-grid> </section>
Component Code
import { Component, ViewChildren, OnInit, AfterViewInit, QueryList } from '@angular/core';import { GridComponent } from '@progress/kendo-angular-grid';export class SearchComponent implements OnInit, AfterViewInit{ //other code emitted for clarity @ViewChildren("searchGrid") public Grids: QueryList<GridComponent> private SearchGrid: GridComponent public ngAfterViewInit(): void { this.Grids.changes.subscribe((comps: QueryList <GridComponent>) => { this.SearchGrid = comps.first; }); }}
Here we are using ViewChildren
on which you can listen for changes. In this case any children with the reference #searchGrid
. Hope this helps.
You could use a setter for @ViewChild()
@ViewChild(FilterTiles) set ft(tiles: FilterTiles) { console.log(tiles);};
If you have an ngIf wrapper, the setter will be called with undefined, and then again with a reference once ngIf allows it to render.
My issue was something else though. I had not included the module containing my "FilterTiles" in my app.modules. The template didn't throw an error but the reference was always undefined.