Typescript error: @viewChild undefined
Tried using the select () method in tabs.ts from the Ionic Tabs documentation. But it seems that when I tried to run it it said "select is undefined" and I found that my viewChild is actually empty / undefined when I tried console.log (tabs). Tried looking for the reason why viewChild is undefined but couldn't figure out why.
Link to ionic tabs documentation: https://ionicframework.com/docs/api/components/tabs/Tabs/
tabs.html
<ion-tabs #tabs>
<ion-tab [root]="tab1Root" tabTitle="Request" tabIcon="alert"></ion-tab>
<ion-tab [root]="tab2Root" [rootParams]="detailParam" tabTitle="Pending"
tabIcon="repeat"></ion-tab>
<ion-tab [root]="tab3Root" tabTitle="Completed" tabIcon="done-all"></ion-
tab>
<ion-tab [root]="tab4Root" tabTitle="Profile" tabIcon="person"></ion-tab>
</ion-tabs>
tabs.ts
import { Component, ViewChild } from '@angular/core';
import { NavController, NavParams, AlertController, Tabs } from 'ionic-
angular';
import { PendingJobPage } from '../pending-job/pending-job';
import { CompletedJobPage } from '../completed-job/completed-job';
import { RequestPage } from '../request/request';
import { ProfilePage } from '../profile/profile';
@Component({
templateUrl: 'tabs.html'
})
export class TabsPage {
@ViewChild('tabs') tabRef: Tabs;
pending: any;
apply: boolean;
detailsParam: any;
tab1Root = RequestPage;
tab2Root = PendingJobPage;
tab3Root = CompletedJobPage;
tab4Root = ProfilePage;
constructor(public navParams: NavParams, public navCtrl: NavController) {
this.pending = this.navParams.get('param1');
this.apply = this.navParams.get('apply');
this.detailsParam = this.navParams.data;
console.log("a = ", this.tabRef);
if(this.apply === true){
this.navCtrl.parent.select(1);
}
else{
this.navCtrl.parent.select(0);
}
}
}
source to share
As you can see in Angular Docs ,
ViewChild is set after view initialization
and
The ViewChild is refreshed after checking the view.
export class AfterViewComponent implements AfterViewChecked, AfterViewInit {
ngAfterViewInit() {
// viewChild is set after the view has been initialized <- Here!
}
ngAfterViewChecked() {
// viewChild is updated after the view has been checked <- Here!
}
// ...
}
So the problem with your code is that the view was not initialized when the constructor was executed. You will need to put all the code that interacts with the tabs in the ngAfterViewInit
lifecycle hook:
ngAfterViewInit() {
// Now you can use the tabs reference
console.log("a = ", this.tabRef);
}
If you just want to use Ionic custom lifecycle events, you need to use a hook ionViewDidEnter
:
export class TabsPage {
@ViewChild('myTabs') tabRef: Tabs;
ionViewDidEnter() {
// Now you can use the tabs reference
console.log("a = ", this.tabRef);
}
}
source to share
Some general approach:
You can create a method that will wait until it ViewChild
is ready
function waitWhileViewChildIsReady(parent: any, viewChildName: string, refreshRateSec: number = 50, maxWaitTime: number = 3000): Observable<any> {
return interval(refreshRateSec)
.pipe(
takeWhile(() => !isDefined(parent[viewChildName])),
filter(x => x === undefined),
takeUntil(timer(maxWaitTime)),
endWith(parent[viewChildName]),
flatMap(v => {
if (!parent[viewChildName]) throw new Error('ViewChild "${viewChildName}" is never ready');
return of(!parent[viewChildName]);
})
);
}
function isDefined<T>(value: T | undefined | null): value is T {
return <T>value !== undefined && <T>value !== null;
}
Using:
// Now you can do it in any place of your code
waitWhileViewChildIsReady(this, 'yourViewChildName').subscribe(() =>{
// your logic here
})
source to share