Call child component method from parent class - Angular

308,357

Solution 1

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();
  }
}

Solution 2

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

Solution 3

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!"; 
  }
 }

Solution 4

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

You have ParentComponent and ChildComponent that looks like this.

parent.component.html

enter image description here

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:

enter image description here

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

Simply do this:

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

enter image description here

The result:

enter image description here

Solution 5

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 
        };
    }
 }
Share:
308,357

Related videos on Youtube

shammelburg
Author by

shammelburg

JavaScript Developer

Updated on July 20, 2022

Comments

  • shammelburg
    shammelburg almost 2 years

    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?

  • Waleed Shahzaib
    Waleed Shahzaib over 6 years
    What is ChildVM in this line: @ViewChild(ChildComponent ) child: ChildVM;
  • Waleed Shahzaib
    Waleed Shahzaib over 6 years
    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.
  • rashfmnb
    rashfmnb over 6 years
    try this link angular.io/guide/component-interaction and comment the directives link
  • Anandhu Ajayakumar
    Anandhu Ajayakumar about 6 years
    How to make it work when there is multiple children of same class??
  • Ajeet Shah
    Ajeet Shah about 6 years
    @WaleedShahzaib I think OP meant ChildComponent by ChildVM
  • Alexei - check Codidact
    Alexei - check Codidact almost 6 years
    It is an elegant solution, however it does not work properly in all cases, probably due Angular change detection not working from subscribe.
  • tatsu
    tatsu over 5 years
    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!
  • Weston
    Weston over 5 years
    Found this to be the best solution for my use case. Works like a charm. Thanks!
  • Trilok Pathak
    Trilok Pathak over 5 years
    @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'."
  • shr
    shr over 5 years
    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.
  • Ambuj Khanna
    Ambuj Khanna over 5 years
    I am always getting Undefined value of "this.child"
  • Imad El Hitti
    Imad El Hitti over 5 years
    @shr any chance you can share your solution to pass an object with callback ?
  • shr
    shr over 5 years
    @ImadElHitti, added a sample solution as a separate answer, since code is not allowed within comments.
  • Kelvin Low
    Kelvin Low about 5 years
    I have the same problem as @AmbujKhanna
  • tony
    tony about 5 years
    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.
  • canbax
    canbax almost 5 years
    OK but we also want to do it programmatically using ts
  • Gil Epshtain
    Gil Epshtain over 4 years
    For use from within the component: @ViewChild('appChild', { static: false }) appChild: ElementRef<HTMLElement>; and later use this.appChild.doSomething()
  • VIKAS KOHLI
    VIKAS KOHLI over 4 years
    This one is elegant solution, this should be the accepted answer, just change import method like import {Subject} from 'rxjs';
  • Tanzeel
    Tanzeel over 4 years
    I'm using your answer with type selector. It worked! :-)
  • Berk Kurkcuoglu
    Berk Kurkcuoglu about 4 years
    A subject is not necessarily required here because it is a one-way relationship, an EventEmitter would be sufficient for this job.
  • Bogdan D
    Bogdan D almost 4 years
    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
  • Bogdan D
    Bogdan D almost 4 years
    Nice, I was looking for something like this, it's a loosely coupling approach. However, the subject in child should be only an Observable, the child must not be able to emit events.
  • Sathiamoorthy
    Sathiamoorthy almost 3 years
    this is the best answer.
  • Adnan Fayaz
    Adnan Fayaz almost 3 years
    There was no need to explicitly call the ngAfterViewInit(). it will be called automatically if implemented on the class
  • lwairore
    lwairore almost 3 years
    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
  • Shadoweb
    Shadoweb over 2 years
    This is indeed the best solution as ViewChild won't work with content projection, you'll have to use ContentChild instead and after a while it can be complex to know which use case, while Subject is consistant.
  • Mateen
    Mateen about 2 years
    NullInjectorError: No provider for ChildComponent!