Angular 2 @ViewChild not working. Cannot Read Property "title" of Undefined
You should be checking for the @ViewChild
in the AfterViewChecked
instead of the AfterViewInit
. Also you can bundle your @angular/core
imports like so:
import { Component, ViewChild, AfterViewInit } from '@angular/core';
. Then instead of implementing AfterViewInit
just implement AfterViewChecked
.
Here is how it could look:
import { AdminComponent } from 'admin/admin.component';
import { Component, ViewChild, AfterViewChecked } from '@angular/core';
@Component({
selector: 'monitor-header',
templateUrl: './header.monitor.component.html',
styleUrls: ['./header.monitor.component.css']
})
export class HeaderMonitorComponent implements AfterViewChecked {
@ViewChild(AdminComponent) private admin: AdminComponent;
private monitorTitle: string;
ngAfterViewChecked() {
this.monitorTitle = this.admin.title;
}
}
One question: Are these both parent components or will one of the components be a child of the other? The reason i ask is that you may want to look into a different method of passing that variable between components as this may be achieved with an @Input
or using a service to store and set the header variable. I hope this can be of some help.
EDIT:
To answer your comment, you should create a service like this:
import { Injectable } from '@angular/core';
@Injectable()
export class HeaderService {
_title: string;
constructor() { }
set title(title) {
this._title = title;
}
get title() {
return this._title;
}
}
Then in your component import the service and get
or set
the variable:
import { AdminComponent } from 'admin/admin.component';
import { Component, ViewChild, AfterViewChecked } from '@angular/core';
import { HeaderService } from 'path/to/your/service';
@Component({
selector: 'monitor-header',
templateUrl: './header.monitor.component.html',
styleUrls: ['./header.monitor.component.css']
})
export class HeaderMonitorComponent implements AfterViewChecked {
@ViewChild(AdminComponent) private admin: AdminComponent;
private monitorTitle: string;
constructor(private headerService: HeaderService) {} // Import service here
ngAfterViewChecked() {
this.monitorTitle = this.headerService.title;
}
}
You just need to make sure you set the title in one of the components using this.headerService.title = 'yourTitle';
I am just not sure which component gets loaded first.
You should check out Angular Services here: https://angular.io/tutorial/toh-pt4
Also check out Angular Component Interaction here: https://angular.io/guide/component-interaction
EDIT #2:
Here is another way to subscribe to that title in your Service:
So here below I have created a Subject
that is of type string, and a method that tells it here is the next string to hold and send.
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class HeaderService {
public titleSubject: Subject<string> = new Subject<string>();
constructor() { }
setNextTitle(title) {
this.titleSubject.next();
}
}
Then in your HeaderMonitorComponent
you would want to subscribe to that Subject
like so:
import { AdminComponent } from 'admin/admin.component';
import { Component, OnInit ViewChild, AfterViewChecked } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { HeaderService } from 'path/to/your/service';
@Component({
selector: 'monitor-header',
templateUrl: './header.monitor.component.html',
styleUrls: ['./header.monitor.component.css']
})
export class HeaderMonitorComponent implements OnInit, AfterViewChecked {
@ViewChild(AdminComponent) private admin: AdminComponent;
private monitorTitle: string;
titleObservable: Observable<string>;
constructor(private headerService: HeaderService) {} // Import service here
ngOnInit() {
this.titleObservable = this.headerService.titleSubject.asObservable();
this.titleObservable.subscribe((title) => {
this.monitorTitle = title;
});
}
ngAfterViewChecked() {
this.monitorTitle = this.headerService.title;
}
}
Then in your AdminComponent
, whenever the button is clicked, call this.headerService.setNextTitle(title)
and the new title which you are subscribing to in the HeaderMonitorComponent will then be acknowledged and replace the current value of monitorTitle
.
Just another quick way to handle the data passing through.
Dheeraj Naik
Updated on June 26, 2022Comments
-
Dheeraj Naik almost 2 years
I am unable to access the property of component even after importing with @ViewChild. Below is the code.
header.monitor.component.ts
import { AdminComponent } from 'admin/admin.component'; import { Component } from '@angular/core'; import { ViewChild, AfterViewInit } from '@angular/core'; @Component({ selector: 'monitor-header', templateUrl: './header.monitor.component.html', styleUrls: ['./header.monitor.component.css'] }) export class HeaderMonitorComponent implements AfterViewInit { @ViewChild(AdminComponent) private admin: AdminComponent; private monitorTitle: string; ngAfterViewInit() { this.monitorTitle = this.admin.title; } }
header.monitor.component.html
<div class="text-center header h3">{{monitorTitle}}</div>
admin.component.ts
import { Component, OnInit, ViewChild } from '@angular/core'; import { ElementRef } from '@angular/core'; @Component({ selector: 'monitor-admin', templateUrl: './admin.component.html', styleUrls: ['./admin.component.css'] }) export class AdminComponent { constructor() { } title = 'Header'; }
ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'title' of undefined TypeError: Cannot read property 'title' of undefined
Help me resolve this error.
-
Pranav C Balan over 6 yearstemplate doesn't contain AdminComponent
-
Developer Thing over 6 yearsThe error message is clear. You are referencing @ViewChild(AdminComponent) without having a reference in your header.monitor.component.html. You don't have <monitor-admin></monitor-admin> and you don't have appropriate template variable (#admin)
-
-
Nicholas Pesa over 6 yearsI also believe you want to explicitly set your title variable to 'public'
-
Dheeraj Naik over 6 yearsYup I explicitly set the variable to public. Yea these both are parent components, I will use @Input property instead of ViewChild. But I do not want to use the view of the admin component, Just the properties of admin component. How can I achieve this ?
-
Nicholas Pesa over 6 yearsI edited my answer above to explain how this can be achieved, but take note you should definitely do some more research on how components interact with data between them.
-
Dheeraj Naik over 6 yearsI want to set the title value in admin component after button click event in admin.component.html and get that value in header component using @Input.
-
Nicholas Pesa over 6 yearsI added another way to change that title on the fly using the
rxjs/Subject
andrxjs/Observable
-
Dheeraj Naik over 6 yearsUsing the final approach I was able to send data only when I injected admin view in the header.component.html. But, when I send data from the separate route of admin view, The header.component is not effected. How to send data with
rxjs/subject
that can effect data in other components on different routes. Note: I specified in @NgModule Providers and as dependency in both components.