3

I'm trying a very simple example of calling childMethod inside the child component from the parent component via @ViewChild() decorator. Unfortunately, the ViewChild variable is always undefined.

Child Component

import {Component, Input, Output, EventEmitter} from '@angular/core';
import {Character} from "../models/character";

@Component({
    selector: 'my-character',
    templateUrl: 'app/components/my.character.component.html'
})
export class MyCharacter {
    @Output() changed: EventEmitter<any> = new EventEmitter();
    @Input() character: Character;

    selectedCharacter: Character;

    select(selectedCharacter: Character) {
        this.selectedCharacter = selectedCharacter;
        this.changed.emit(selectedCharacter);
    };

    childMethod() {
        console.log('This method is called from the parent component via ViewChild');
    };
}

Parent Component

import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { Character } from '../models/character';
import { MyCharacter } from "../components/my.character.component";

@Component({
    selector: 'character-list',
    templateUrl: 'app/components/character.list.component.html',
    directives: [ MyCharacter ]
})
export class CharacterList implements AfterViewInit{
    selectedCharacter: Character;

    @ViewChild(MyCharacter) myChar:MyCharacter;

    ngOnInit() {
      console.log('on init');
    };

    ngAfterViewInit() {
        console.log('after init');
    };

    characters = [
        new Character(1, 'Han Solo'),
        new Character(2, 'Luke Skywalker'),
        new Character(3, 'BB-8'),
        new Character(4, 'Rey')
    ];
    select(selectedCharacter: Character) {
        this.selectedCharacter = selectedCharacter;
        this.myChar.childMethod();
    }

    changed (event: any) {
        console.log('Hello! There is a change in the item.');
    }
}

parent component html

<h2>Characters</h2>

<ul>
    <li *ngFor="let character of characters" (click)="select(character)">
        {{character.name}}
    </li>
</ul>

<my-character *ngIf="selectedCharacter" [character]="selectedCharacter" (changed)="changed($event)"></my-character>

Error I am getting when I invoke click method

core.umd.js:3462 EXCEPTION: Error in app/components/character.list.component.html:3:45 caused by: Cannot read property 'childMethod' of undefinedErrorHandler.handleError @ core.umd.js:3462next @ core.umd.js:6924schedulerFn @ core.umd.js:6172SafeSubscriber.__tryOrUnsub @ Subscriber.ts:238SafeSubscriber.next @ Subscriber.ts:190Subscriber._next @ Subscriber.ts:135Subscriber.next @ Subscriber.ts:95Subject.next @ Subject.ts:61EventEmitter.emit @ core.umd.js:6164onError @ core.umd.js:6388onHandleError @ core.umd.js:6263ZoneDelegate.handleError @ zone.js:207Zone.runGuarded @ zone.js:113NgZoneImpl.runInnerGuarded @ core.umd.js:6271NgZone.runGuarded @ core.umd.js:6504outsideHandler @ platform-browser.umd.js:1990ZoneDelegate.invokeTask @ zone.js:236Zone.runTask @ zone.js:136ZoneTask.invoke @ zone.js:304
core.umd.js:3464 ORIGINAL EXCEPTION: Cannot read property 'childMethod' of undefinedErrorHandler.handleError @ core.umd.js:3464next @ core.umd.js:6924schedulerFn @ core.umd.js:6172SafeSubscriber.__tryOrUnsub @ Subscriber.ts:238SafeSubscriber.next @ Subscriber.ts:190Subscriber._next @ Subscriber.ts:135Subscriber.next @ Subscriber.ts:95Subject.next @ Subject.ts:61EventEmitter.emit @ core.umd.js:6164onError @ core.umd.js:6388onHandleError @ core.umd.js:6263ZoneDelegate.handleError @ zone.js:207Zone.runGuarded @ zone.js:113NgZoneImpl.runInnerGuarded @ core.umd.js:6271NgZone.runGuarded @ core.umd.js:6504outsideHandler @ platform-browser.umd.js:1990ZoneDelegate.invokeTask @ zone.js:236Zone.runTask @ zone.js:136ZoneTask.invoke @ zone.js:304
core.umd.js:3467 ORIGINAL STACKTRACE:ErrorHandler.handleError @ core.umd.js:3467next @ core.umd.js:6924schedulerFn @ core.umd.js:6172SafeSubscriber.__tryOrUnsub @ Subscriber.ts:238SafeSubscriber.next @ Subscriber.ts:190Subscriber._next @ Subscriber.ts:135Subscriber.next @ Subscriber.ts:95Subject.next @ Subject.ts:61EventEmitter.emit @ core.umd.js:6164onError @ core.umd.js:6388onHandleError @ core.umd.js:6263ZoneDelegate.handleError @ zone.js:207Zone.runGuarded @ zone.js:113NgZoneImpl.runInnerGuarded @ core.umd.js:6271NgZone.runGuarded @ core.umd.js:6504outsideHandler @ platform-browser.umd.js:1990ZoneDelegate.invokeTask @ zone.js:236Zone.runTask @ zone.js:136ZoneTask.invoke @ zone.js:304
core.umd.js:3468 TypeError: Cannot read property 'childMethod' of undefined
    at CharacterList.select (character.list.component.ts:31)
    at DebugAppView._View_CharacterList1._handle_click_0_0 (CharacterList.ngfactory.js:157)
    at eval (core.umd.js:9698)
    at eval (platform-browser.umd.js:1877)
    at eval (platform-browser.umd.js:1990)
    at ZoneDelegate.invoke (zone.js:203)
    at Object.onInvoke (core.umd.js:6242)
    at ZoneDelegate.invoke (zone.js:202)
    at Zone.runGuarded (zone.js:110)
    at NgZoneImpl.runInnerGuarded (core.umd.js:6271)ErrorHandler.handleError @ core.umd.js:3468next @ core.umd.js:6924schedulerFn @ core.umd.js:6172SafeSubscriber.__tryOrUnsub @ Subscriber.ts:238SafeSubscriber.next @ Subscriber.ts:190Subscriber._next @ Subscriber.ts:135Subscriber.next @ Subscriber.ts:95Subject.next @ Subject.ts:61EventEmitter.emit @ core.umd.js:6164onError @ core.umd.js:6388onHandleError @ core.umd.js:6263ZoneDelegate.handleError @ zone.js:207Zone.runGuarded @ zone.js:113NgZoneImpl.runInnerGuarded @ core.umd.js:6271NgZone.runGuarded @ core.umd.js:6504outsideHandler @ platform-browser.umd.js:1990ZoneDelegate.invokeTask @ zone.js:236Zone.runTask @ zone.js:136ZoneTask.invoke @ zone.js:304
core.umd.js:3471 ERROR CONTEXT:ErrorHandler.handleError @ core.umd.js:3471next @ core.umd.js:6924schedulerFn @ core.umd.js:6172SafeSubscriber.__tryOrUnsub @ Subscriber.ts:238SafeSubscriber.next @ Subscriber.ts:190Subscriber._next @ Subscriber.ts:135Subscriber.next @ Subscriber.ts:95Subject.next @ Subject.ts:61EventEmitter.emit @ core.umd.js:6164onError @ core.umd.js:6388onHandleError @ core.umd.js:6263ZoneDelegate.handleError @ zone.js:207Zone.runGuarded @ zone.js:113NgZoneImpl.runInnerGuarded @ core.umd.js:6271NgZone.runGuarded @ core.umd.js:6504outsideHandler @ platform-browser.umd.js:1990ZoneDelegate.invokeTask @ zone.js:236Zone.runTask @ zone.js:136ZoneTask.invoke @ zone.js:304
core.umd.js:3472 DebugContext {_view: _View_CharacterList1, _nodeIndex: 0, _tplRow: 3, _tplCol: 45}ErrorHandler.handleError @ core.umd.js:3472next @ core.umd.js:6924schedulerFn @ core.umd.js:6172SafeSubscriber.__tryOrUnsub @ Subscriber.ts:238SafeSubscriber.next @ Subscriber.ts:190Subscriber._next @ Subscriber.ts:135Subscriber.next @ Subscriber.ts:95Subject.next @ Subject.ts:61EventEmitter.emit @ core.umd.js:6164onError @ core.umd.js:6388onHandleError @ core.umd.js:6263ZoneDelegate.handleError @ zone.js:207Zone.runGuarded @ zone.js:113NgZoneImpl.runInnerGuarded @ core.umd.js:6271NgZone.runGuarded @ core.umd.js:6504outsideHandler @ platform-browser.umd.js:1990ZoneDelegate.invokeTask @ zone.js:236Zone.runTask @ zone.js:136ZoneTask.invoke @ zone.js:304
zone.js:140 Uncaught Error: Error in app/components/character.list.component.html:3:45 caused by: Cannot read property 'childMethod' of undefined

Any clarification would be helpful.

1 Answer 1

5

This is the culprit:

*ngIf="selectedCharacter"

That ngIf will prevent the ViewChild from being instantiated before it's too late. You can move it to inside MyCharacter's template to have the ViewChild reference in parent component without changing any behaviour of your app.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you very much. I do not understand the 'Why' behind it though :( It was possible in Angular 1.x
Uhm I have no AngularJS 1.x experience so can't confirm it. But here, when you use *ngIf and your condition is false, the DOM will not be there and you're referencing to nothing. When you change the condition (selectedCharacter) to enable that DOM, it will take Angular2 a while to instantiate your ViewChild and that's why you cannot immediately call its function like what you did in the function.
you rock!!! Thanks Harry! This is the exact reason. I manually set the ngIf to true and checked. It worked.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.