1

I have a function on click event at parent html.

<span (click)="onFilterClick()" class="filter-icon">
<mat-icon>keyboard_arrow_down</mat-icon></span>
<m-scp-filter-template [openFilter]="true" *ngIf="templateFor === 'col1' "></m-scp-filter-template>

child component at parent ts

@ViewChild(ScpFilterTemplateComponent) myChild;

the function at parent ts

onFilterClick() {
  this.myChild.openMenu();
}

and called function at child ts

openMenu() {
     console.log('successfully executed.');
}

but I getting error

'TypeError: Cannot read property 'openMenu' of undefined at ScpDataTableComponent.'

weka bird
  • 55
  • 8

4 Answers4

1
  • As @ViewChild refers to the existing child view/ selector, your child view needs to exist in the parent HTML. If your *ngIf returns false, Angular won't even create a child view in the parent HTML. So your myChild itself would be undefined in that case & you won't be able to access any methods/ properties further.
  • There is an alternate bypass too - that you can create an instance of your child class & call the method, but it's not recommended.

Here's the minimal reproduction of both the scenarios.

Tushar Walzade
  • 3,737
  • 4
  • 33
  • 56
  • this is not the same issue – ABOS Jan 29 '19 at 15:01
  • nope, ngIf might be causing the issue which is missing in your demo, so OP might need to wrap `this.myChild.openMenu();` inside a timeout etc. – ABOS Jan 29 '19 at 15:10
0

Here is a copy from the Angular documentation https://angular.io/guide/component-interaction#parent-calls-an-viewchild

import { AfterViewInit, ViewChild } from '@angular/core';
import { Component }                from '@angular/core';
import { CountdownTimerComponent }  from './countdown-timer.component';

@Component({
  selector: 'app-countdown-parent-vc',
  template: `
  <h3>Countdown to Liftoff (via ViewChild)</h3>
  <button (click)="start()">Start</button>
  <button (click)="stop()">Stop</button>
  <div class="seconds">{{ seconds() }}</div>
  <app-countdown-timer></app-countdown-timer>
  `,
  styleUrls: ['../assets/demo.css']
})
export class CountdownViewChildParentComponent implements AfterViewInit {

  @ViewChild(CountdownTimerComponent)
  private timerComponent: CountdownTimerComponent;

  seconds() { return 0; }

  ngAfterViewInit() {
    // Redefine `seconds()` to get from the `CountdownTimerComponent.seconds` ...
    // but wait a tick first to avoid one-time devMode
    // unidirectional-data-flow-violation error
    setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
  }

  start() { this.timerComponent.start(); }
  stop() { this.timerComponent.stop(); }
}

In the example the start() and stop() method call a the respective methods of the child component when the buttons of the parent component are clicked


If the child component has a *ngIf set onto it you can probably fix it by Angular 2 @ViewChild in *ngIf

Jelle
  • 2,026
  • 1
  • 12
  • 17
0

Can you try with a setter?

@ViewChild(ScpFilterTemplateComponent) 
set _myChild(v) {
  setTimeout(() => { this.myChild= v; }, 0);
}

or wrap this.myChild.openMenu(); inside a timeout etc

ABOS
  • 3,723
  • 3
  • 17
  • 23
0

try making that child method public...

public openMenu() {
     console.log('successfully executed.');
}
John Scott
  • 85
  • 1
  • 9