Llamar al método de componente secundario desde la clase principal - Angular

130

He creado un componente secundario que tiene un método que quiero invocar.

Cuando invoco este método, solo activa la console.log()línea, ¿no establecerá la testpropiedad?

A continuación se muestra la aplicación Angular de inicio rápido con mis cambios.

Padre

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

Niño

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

¿Cómo puedo configurar la testpropiedad también?

Shammelburg
fuente
Posible duplicado de Llamar a un método del componente hijo
Damjan Pavlica
Puede consultar esta respuesta aquí: stackoverflow.com/a/53057589/6663458
Muhammad Mabrouk

Respuestas:

210

Puede hacer esto usando @ViewChildpara obtener más información, consulte este enlace

Con selector de tipo

componente hijo

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

componente padre

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

  @ViewChild(ChildCmp) child:ChildCmp;

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

Con selector de cuerdas

componente hijo

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

componente padre

@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();
  }
}
rashfmnb
fuente
6
Seguí su enfoque, pero obtengo un error al usar directivas: [ChildCmp], El error dice: directives 'no existe en el tipo' Component '. Lo busqué en Google y encontré que las directivas están obsoletas en rc5. Entonces, ¿cómo manejarlo en la versión más nueva? Por favor ayuda.
Waleed Shahzaib
1
pruebe este enlace angular.io/guide/component-interaction y comente el enlace de directivas
rashfmnb
5
¿Cómo hacer que funcione cuando hay varios niños de la misma clase?
Anandhu Ajayakumar
@rashfmnb "Declaración esperada". Se produce un error cuando intento escribir @ViewChild ('child') child: ChildCmp; en el componente. ¡Por favor ayuda! Y también no puedo importar lo mismo en la directiva, me da un error como "directiva: (typeof EmployeeProfileC ... 'no se puede asignar al parámetro de tipo' Componente '. El literal de objeto solo puede especificar propiedades conocidas, y la' directiva 'no existen en el tipo 'Componente'. "
Trilok Pathak
1
Esta es una respuesta correcta, pero produce componentes estrechamente acoplados . Un mejor patrón es usar Inputpropiedades: un observable al que el niño reacciona llamando a su propia función interna. Ver la respuesta del usuario 6779899
Bogdan D
56

¡Esto funcionó para mí! Para Angular 2, llamar al método del componente secundario en el componente principal

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

Kaur A
fuente
4
¿Qué es ChildVM en esta línea: @ViewChild (ChildComponent) child: ChildVM;
Waleed Shahzaib
@WaleedShahzaib Creo OP entiende ChildComponentporChildVM
ajeet Shah
1
Pensé que esto crearía una instancia separada del componente, pero en realidad llama a la función desde su instancia con sus variables en el estado actual de ese componente, ¡vaca sagrada! ¡este método es mucho mejor que la primera respuesta!
tatsu
3
Siempre obtengo un valor indefinido de "this.child"
Ambuj Khanna
2
Supongo que 'this.child' no está definido es que ViewChild está apuntando a algo que no existe en la plantilla, o está intentando acceder a él demasiado pronto en el ciclo de vida, por ejemplo, en el constructor.
Tony
34

Creo que la forma más sencilla es utilizar Subject. En el siguiente código de ejemplo, se notificará al niño cada vez que se llame a 'tellChild'.

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

Muestra de trabajo en Stackblitz

usuario6779899
fuente
4
Es una solución elegante, sin embargo, no funciona correctamente en todos los casos, probablemente debido a que la detección de cambios angulares no funciona desde la suscripción.
Alexei
1
Descubrí que esta es la mejor solución para mi caso de uso. Funciona de maravilla. ¡Gracias!
Weston
Ordenado ! Para casos más simples, puede evitar la sobrecarga de Asunto / Suscripción pasando un objeto que tenga un método de devolución de llamada al niño. De forma similar a lo anterior, el hijo anula la devolución de llamada para recibir indicaciones del padre.
Shr
@shr ¿alguna posibilidad de que pueda compartir su solución para pasar un objeto con devolución de llamada?
Imad El Hitti
1
Esta es una solución elegante, esta debería ser la respuesta aceptada, simplemente cambie el método de importación como import {Subject} de 'rxjs';
VIKAS KOHLI
5

Angular: llamar al método del componente secundario en la plantilla del componente principal

Tiene ParentComponent y ChildComponent que se ve así.

parent.component.html

ingrese la descripción de la imagen aquí

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

Cuando se sirve, se ve así:

ingrese la descripción de la imagen aquí

Cuando el usuario se centra en el elemento de entrada de ParentComponent, desea llamar al método doSomething () de ChildComponent.

Simplemente haz esto:

  1. Dale al selector de app-child en parent.component.html un nombre de variable DOM (prefijo con # - hashtag) , en este caso lo llamamos appChild.
  2. Asigne un valor de expresión (del método que desea llamar) al evento de enfoque del elemento de entrada.

ingrese la descripción de la imagen aquí

El resultado:

ingrese la descripción de la imagen aquí

Hemant Ramphul
fuente
Está bien, pero también queremos hacerlo programáticamente usando ts
canbax
Para uso desde el interior del componente: @ViewChild('appChild', { static: false }) appChild: ElementRef<HTMLElement>;y uso posteriorthis.appChild.doSomething()
Gil Epshtain
4

La respuesta de user6779899 es ordenada y más genérica Sin embargo, según la solicitud de Imad El Hitti, aquí se propone una solución liviana. Esto se puede utilizar cuando un componente secundario está estrechamente conectado a un solo padre.

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 
        };
    }
 }
Shr
fuente
2

Considere el siguiente ejemplo,

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

Lea más sobre @ViewChild aquí.

lwairore
fuente
0

Tuve una situación exacta en la que el componente principal tenía un Selectelemento en un formulario y, al enviarlo, necesitaba llamar al método del componente secundario correspondiente de acuerdo con el valor seleccionado del elemento de selección.

Parent.HTML:

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

Padre.TS:

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

Niño.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(){ }
}

Espero que esto ayude.

Vibhor Dube
fuente