ChatGPT解决这个技术问题 Extra ChatGPT

Call child component method from parent class - Angular

I have created a child component which has a method I want to invoke.

When I invoke this method it only fires the console.log() line, it will not set the test property??

Below is the quick start Angular app with my changes.

Parent

import { Component } from '@angular/core';
import { NotifyComponent }  from './notify.component';

@Component({
    selector: 'my-app',
    template:
    `
    <button (click)="submit()">Call Child Component Method</button>
    `
})
export class AppComponent {
    private notify: NotifyComponent;

    constructor() { 
      this.notify = new NotifyComponent();
    }

    submit(): void {
        // execute child component method
        notify.callMethod();
    }
}

Child

import { Component, OnInit } from '@angular/core';

@Component({
    selector: 'notify',
    template: '<h3>Notify {{test}}</h3>'
})
export class NotifyComponent implements OnInit {
   test:string; 
   constructor() { }

    ngOnInit() { }

    callMethod(): void {
        console.log('successfully executed.');
        this.test = 'Me';
    }
}

How can I set the test property as well?

Possible duplicate of Call a method of the child component
You can check this answer here: stackoverflow.com/a/53057589/6663458

M
Muhammad Mabrouk

You can do this by using @ViewChild for more info check this link

With type selector

child component

@Component({
  selector: 'child-cmp',
  template: '<p>child</p>'
})
class ChildCmp {
  doSomething() {}
}

parent component

@Component({
  selector: 'some-cmp',
  template: '<child-cmp></child-cmp>',
  directives: [ChildCmp]
})
class SomeCmp {

  @ViewChild(ChildCmp) child:ChildCmp;

  ngAfterViewInit() {
    // child is set
    this.child.doSomething();
  }
}

With string selector

child component

@Component({
  selector: 'child-cmp',
  template: '<p>child</p>'
})
class ChildCmp {
  doSomething() {}
}

parent component

@Component({
  selector: 'some-cmp',
  template: '<child-cmp #child></child-cmp>',
  directives: [ChildCmp]
})
class SomeCmp {

  @ViewChild('child') child:ChildCmp;

  ngAfterViewInit() {
    // child is set
    this.child.doSomething();
  }
}

I followed your approach, but I'm getting error while using directives: [ChildCmp], The error says: directives' does not exist in type 'Component'. I've googled it and found directives is deprecated in rc5. So how to handle it on the newer version. Please help.
try this link angular.io/guide/component-interaction and comment the directives link
How to make it work when there is multiple children of same class??
@rashfmnb "Declaration expected". An error is coming when I try to write @ViewChild('child') child:ChildCmp;in the component. Please help! And Also i cannot import the same in the directive it gives me error like "directive: (typeof EmployeeProfileC...' is not assignable to parameter of type 'Component'. Object literal may only specify known properties, and 'directive' does not exist in type 'Component'."
This a correct answer, but it produces tightly coupled components. A better pattern is to use Input properties: an observable to which the child reacts by calling its own internal function. See user6779899 's answer
A
Aljosha Koecher

I think most easy way is using Subject. In below example code, the child will be notified each time 'tellChild()' is called.

Parent.component.ts

import {Subject} from 'rxjs/Subject';
...
export class ParentComp {
  changingValue: Subject<boolean> = new Subject();
        
  tellChild() {
    this.changingValue.next(true);
  }
}

Parent.component.html

<my-comp [changing]="changingValue"></my-comp>

Child.component.ts

...
export class ChildComp implements OnInit{
  @Input() changing: Subject<boolean>;
  
  ngOnInit(){
    this.changing.subscribe(v => { 
      console.log('value is changing', v);
    });
  }
}

Working sample on Stackblitz


It is an elegant solution, however it does not work properly in all cases, probably due Angular change detection not working from subscribe.
Found this to be the best solution for my use case. Works like a charm. Thanks!
Neat ! For simpler cases, you can avoid the Subject/Subscribe overhead by passing an object that has a callback method to the child. Similar to the above, the child overrides the callback to receive indications from the parent.
@shr any chance you can share your solution to pass an object with callback ?
This one is elegant solution, this should be the accepted answer, just change import method like import {Subject} from 'rxjs';
R
Reinstate Monica

This Worked for me ! For Angular 2 , Call child component method in parent component

Parent.component.ts

    import { Component, OnInit, ViewChild } from '@angular/core';
    import { ChildComponent } from '../child/child'; 
    @Component({ 
               selector: 'parent-app', 
               template: `<child-cmp></child-cmp>` 
              }) 
    export class parentComponent implements OnInit{ 
        @ViewChild(ChildComponent ) child: ChildComponent ; 

        ngOnInit() { 
           this.child.ChildTestCmp(); } 
}

Child.component.ts

import { Component } from '@angular/core';
@Component({ 
  selector: 'child-cmp', 
  template: `<h2> Show Child Component</h2><br/><p> {{test }}</p> ` 
})
export class ChildComponent {
  test: string;
  ChildTestCmp() 
  { 
    this.test = "I am child component!"; 
  }
 }


What is ChildVM in this line: @ViewChild(ChildComponent ) child: ChildVM;
@WaleedShahzaib I think OP meant ChildComponent by ChildVM
I thought this would create a separate instance of the component but it actually calls the function from your instance with it's variables in the current state of that component, holy cow! this method is way better than the first answer!
I am always getting Undefined value of "this.child"
My guess for 'this.child' being undefined is that either ViewChild is pointing at something that doesn't exist in the template, or you are trying to access it too early in the lifecycle, e.g. in the constructor.
H
Hemant Ramphul

Angular – Call Child Component’s Method in Parent Component’s Template

You have ParentComponent and ChildComponent that looks like this.

parent.component.html

https://i.stack.imgur.com/40D0r.png

parent.component.ts

import {Component} from '@angular/core';

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.css']
})
export class ParentComponent {
  constructor() {
  }
}

child.component.html

<p>
  This is child
</p>

child.component.ts

import {Component} from '@angular/core';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css']
})
export class ChildComponent {
  constructor() {
  }

  doSomething() {
    console.log('do something');
  }
}

When serve, it looks like this:

https://i.stack.imgur.com/rnywq.png

When user focus on ParentComponent’s input element, you want to call ChildComponent’s doSomething() method.

Simply do this:

Give app-child selector in parent.component.html a DOM variable name (prefix with # – hashtag), in this case we call it appChild. Assign expression value (of the method you want to call) to input element’s focus event.

https://i.stack.imgur.com/jixZC.png

The result:

https://i.stack.imgur.com/qC1Cg.gif


OK but we also want to do it programmatically using ts
For use from within the component: @ViewChild('appChild', { static: false }) appChild: ElementRef<HTMLElement>; and later use this.appChild.doSomething()
s
shr

user6779899's answer is neat and more generic However, based on the request by Imad El Hitti, a light weight solution is proposed here. This can be used when a child component is tightly connected to one parent only.

Parent.component.ts

export class Notifier {
    valueChanged: (data: number) => void = (d: number) => { };
}

export class Parent {
    notifyObj = new Notifier();
    tellChild(newValue: number) {
        this.notifyObj.valueChanged(newValue); // inform child
    }
}

Parent.component.html

<my-child-comp [notify]="notifyObj"></my-child-comp>

Child.component.ts

export class ChildComp implements OnInit{
    @Input() notify = new Notifier(); // create object to satisfy typescript
    ngOnInit(){
      this.notify.valueChanged = (d: number) => {
            console.log(`Parent has notified changes to ${d}`);
            // do something with the new value 
        };
    }
 }

l
lwairore

Consider the following example,

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

@Component({
    selector: 'app-countdown-parent-vc',
    templateUrl: 'app-countdown-parent-vc.html',
    styleUrl: [app-countdown-parent-vc.css]
})
export class CreateCategoryComponent implements OnInit, AfterViewInit {
    @ViewChild(CountdownTimerComponent, {static: false}) private timerComponent: CountdownTimerComponent;
    ngAfterViewInit() {
        this.timerComponent.startTimer();
    }

    submitNewCategory(){
        this.ngAfterViewInit();
    }
}

Read more about @ViewChild here.


this is the best answer.
There was no need to explicitly call the ngAfterViewInit(). it will be called automatically if implemented on the class
There's a need to explicitly call the ngAfterViewInit() because we explicitly need to tell Angular to call method startTimer(), which is in component CountdownTimerComponent
M
Manjunath Hadimani

parent.component.html

<app-child #childComponent></app-child>

parent.component.ts

@Component({
    selector: 'app-parent',
    templateUrl: './app-parent.component.html',
    styleUrls: ['./app-parent.component.scss']
})
export class ParentComponent {
    @ViewChild('childComponent', {static: false}) childComponent: ChildComponent;

    anyMethod(): void {
        childComponent.updateData() // updateData is a child method
    }
}

child.component.ts

@Component({
    selector: 'app-child',
    templateUrl: './app-child.component.html',
    styleUrls: ['./app-child.component.scss']
})
export class ChildComponent {
    updateData(): void {
      // Method code goes here
    }
}

V
Vibhor Dube

I had an exact situation where the Parent-component had a Select element in a form and on submit, I needed to call the relevant Child-Component's method according to the selected value from the select element.

Parent.HTML:

<form (ngSubmit)='selX' [formGroup]="xSelForm">
    <select formControlName="xSelector">
      ...
    </select>
<button type="submit">Submit</button>
</form>
<child [selectedX]="selectedX"></child>

Parent.TS:

selX(){
  this.selectedX = this.xSelForm.value['xSelector'];
}

Child.TS:

export class ChildComponent implements OnChanges {
  @Input() public selectedX;

  //ngOnChanges will execute if there is a change in the value of selectedX which has been passed to child as an @Input.

  ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
    this.childFunction();
  }
  childFunction(){ }
}

Hope this helps.