Appeler la méthode du composant enfant à partir de la classe parent - Angular


130

J'ai créé un composant enfant qui a une méthode que je souhaite appeler.

Lorsque j'invoque cette méthode, il ne déclenche que la console.log()ligne, il ne définira pas la testpropriété ??

Vous trouverez ci-dessous l'application Angular de démarrage rapide avec mes modifications.

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

Enfant

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';
    }
}

Comment puis-je définir la testpropriété également?



Vous pouvez vérifier cette réponse ici: stackoverflow.com/a/53057589/6663458
Muhammad Mabrouk

Réponses:


210

Vous pouvez le faire en utilisant @ViewChildpour plus d'informations, consultez ce lien

Avec sélecteur de type

composant enfant

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

composant parent

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

  @ViewChild(ChildCmp) child:ChildCmp;

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

Avec sélecteur de cordes

composant enfant

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

composant parent

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

6
J'ai suivi votre approche, mais j'obtiens une erreur lors de l'utilisation des directives: [ChildCmp], l'erreur dit: les directives 'n'existe pas dans le type' Component '. Je l'ai googlé et j'ai trouvé que les directives sont obsolètes dans rc5. Alors, comment le gérer sur la nouvelle version. Veuillez aider.
Waleed Shahzaib

1
essayez ce lien angular.io/guide/component-interaction et commentez le lien des directives
rashfmnb

5
Comment le faire fonctionner quand il y a plusieurs enfants de la même classe ??
Anandhu Ajayakumar

@rashfmnb "Déclaration attendue". Une erreur survient lorsque j'essaye d'écrire @ViewChild ('child') child: ChildCmp; dans le composant. Veuillez aider! Et aussi je ne peux pas importer la même chose dans la directive, cela me donne une erreur comme "directive: (typeof EmployeeProfileC ... 'n'est pas assignable au paramètre de type' Component '. L'objet littéral ne peut spécifier que des propriétés connues, et' directive 'ne le fait pas existent dans le type 'Component'. "
Trilok Pathak

1
C'est une réponse correcte, mais elle produit des composants étroitement couplés . Un meilleur modèle est d'utiliser des Inputpropriétés: une observable à laquelle l'enfant réagit en appelant sa propre fonction interne. Voir la réponse de user6779899
Bogdan D

56

Cela a fonctionné pour moi! Pour Angular 2, appelez la méthode du composant enfant dans le composant parent

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


4
Qu'est-ce que ChildVM dans cette ligne: @ViewChild (ChildComponent) child: ChildVM;
Waleed Shahzaib

@WaleedShahzaib Je pense que OP voulait dire ChildComponentparChildVM
Ajeet Shah

1
Je pensais que cela créerait une instance distincte du composant, mais cela appelle en fait la fonction de votre instance avec ses variables dans l'état actuel de ce composant, Holy Cow! cette méthode est bien meilleure que la première réponse!
tatsu

3
Je reçois toujours une valeur indéfinie de "this.child"
Ambuj Khanna

2
Je suppose que «this.child» n'est pas défini, c'est que soit ViewChild pointe vers quelque chose qui n'existe pas dans le modèle, soit vous essayez d'y accéder trop tôt dans le cycle de vie, par exemple dans le constructeur.
tony

34

Je pense que le moyen le plus simple est d'utiliser Subject. Dans l'exemple de code ci-dessous, l'enfant sera averti chaque fois que 'tellChild' est appelé.

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

Échantillon de travail sur Stackblitz


4
C'est une solution élégante, mais elle ne fonctionne pas correctement dans tous les cas, probablement en raison de la détection de changement angulaire qui ne fonctionne pas depuis l'abonnement.
Alexei

1
J'ai trouvé que c'était la meilleure solution pour mon cas d'utilisation. Fonctionne comme un charme. Merci!
Weston

Soigné ! Pour les cas plus simples, vous pouvez éviter la surcharge Sujet / Abonnement en passant un objet doté d'une méthode de rappel à l'enfant. Similaire à ce qui précède, l'enfant remplace le rappel pour recevoir des indications du parent.
shr

@shr une chance que vous puissiez partager votre solution pour passer un objet avec rappel?
Imad El Hitti

1
Celui-ci est une solution élégante, cela devrait être la réponse acceptée, changez simplement la méthode d'importation comme import {Subject} de 'rxjs';
VIKAS KOHLI

5

Angular - Appeler la méthode du composant enfant dans le modèle du composant parent

Vous avez ParentComponent et ChildComponent qui ressemble à ceci.

parent.component.html

entrez la description de l'image ici

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

Au service, cela ressemble à ceci:

entrez la description de l'image ici

Lorsque l'utilisateur se concentre sur l'élément d'entrée de ParentComponent, vous souhaitez appeler la méthode doSomething () de ChildComponent.

Faites simplement ceci:

  1. Donnez au sélecteur app-child dans parent.component.html un nom de variable DOM (préfixe avec # - hashtag) , dans ce cas, nous l'appelons appChild.
  2. Attribuez une valeur d'expression (de la méthode que vous souhaitez appeler) à l'événement de focus de l'élément d'entrée.

entrez la description de l'image ici

Le résultat:

entrez la description de l'image ici


OK mais nous voulons aussi le faire par programme en utilisant ts
canbax

Pour une utilisation à partir du composant: @ViewChild('appChild', { static: false }) appChild: ElementRef<HTMLElement>;et une utilisation ultérieurethis.appChild.doSomething()
Gil Epshtain

4

La réponse de user6779899 est nette et plus générique Cependant, sur la base de la demande d'Imad El Hitti, une solution légère est proposée ici. Cela peut être utilisé lorsqu'un composant enfant est étroitement connecté à un seul parent.

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

2

Prenons l'exemple suivant,

    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 {
         @ViewChild(CountdownTimerComponent, {static: false})
         private timerComponent: CountdownTimerComponent;
         ngAfterViewInit() {
             this.timerComponent.startTimer();
         }

         submitNewCategory(){
            this.ngAfterViewInit();     
         }

En savoir plus sur @ViewChild ici.


0

J'avais une situation exacte où le composant Parent avait un Selectélément dans un formulaire et lors de la soumission, je devais appeler la méthode du composant enfant correspondant en fonction de la valeur sélectionnée à partir de l'élément de sélection.

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'];
}

Enfant.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(){ }
}

J'espère que cela t'aides.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.