Chamar método de componente filho da classe pai - Angular

130

Eu criei um componente filho que possui um método que eu quero chamar.

Quando invoco esse método, ele dispara apenas a console.log()linha, não define otest propriedade ??

Abaixo está o aplicativo Angular de início rápido com minhas alterações.

Pai

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

Criança

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

Como posso definir a testpropriedade também?

Shammelburg
fonte
Possível duplicado de chamar um método do componente filho
Damjan Pavlica
Você pode verificar esta resposta aqui: stackoverflow.com/a/53057589/6663458
Muhammad Mabrouk

Respostas:

209

Você pode fazer isso usando, @ViewChildpara obter mais informações, consulte este link

Com seletor de tipo

componente filho

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

componente pai

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

  @ViewChild(ChildCmp) child:ChildCmp;

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

Com seletor de cordas

componente filho

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

componente pai

@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
fonte
6
Eu segui sua abordagem, mas estou recebendo erro ao usar diretivas: [ChildCmp], o erro diz: diretivas 'não existe no tipo' Componente '. Eu pesquisei no Google e achei diretivas obsoletas no rc5. Então, como lidar com isso na versão mais recente. Por favor ajude.
Waleed Shahzaib
1
tente este link angular.io/guide/component-interaction e comente o link das diretivas
rashfmnb
5
Como fazê-lo funcionar quando há vários filhos da mesma classe?
Anandhu Ajayakumar
@rashfmnb "Declaração esperada". Está ocorrendo um erro ao tentar escrever o filho @ViewChild ('child'): ChildCmp; no componente. Por favor ajude! E também não posso importar o mesmo na diretiva, isso me dá erro como "diretiva: (typeof EmployeeProfileC ... 'não é atribuível ao parâmetro do tipo' Component '. O literal do objeto pode especificar apenas propriedades conhecidas e a diretiva' não existe no tipo 'Component'. "
Trilok Pathak
1
Essa é uma resposta correta, mas produz componentes fortemente acoplados . Um padrão melhor é usar Inputpropriedades: um observável ao qual a criança reage chamando sua própria função interna. Veja a resposta de user6779899
Bogdan D
56

Isso funcionou para mim! Para Angular 2, chame o método do componente filho no componente pai

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
fonte
4
O que é ChildVM nesta linha: @ViewChild (ChildComponent) child: ChildVM;
Waleed Shahzaib
@WaleedShahzaib Eu acho que OP significava ChildComponentporChildVM
Ajeet Shah 18/18/18
1
Eu pensei que isso criaria uma instância separada do componente, mas na verdade chama a função da sua instância com suas variáveis ​​no estado atual desse componente, vaca sagrada! esse método é muito melhor que a primeira resposta!
tatsu 18/09/18
3
Estou sempre recebendo o valor indefinido de "this.child"
Ambuj Khanna
2
Meu palpite para que 'this.child' seja indefinido é que o ViewChild está apontando para algo que não existe no modelo ou você está tentando acessá-lo muito cedo no ciclo de vida, por exemplo, no construtor.
Tony
34

Eu acho que a maneira mais fácil é usar o Subject. No código de exemplo abaixo, o filho será notificado toda vez que 'tellChild' for chamado.

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

Amostra de trabalho no Stackblitz

user6779899
fonte
4
É uma solução elegante, no entanto, não funciona corretamente em todos os casos, provavelmente devido à detecção de alterações angulares não estar funcionando na assinatura.
Alexei
1
Constatou que esta é a melhor solução para o meu caso de uso. Funciona como um encanto. Obrigado!
Weston
Arrumado ! Para casos mais simples, você pode evitar a sobrecarga de Assunto / Assinatura passando um objeto que possui um método de retorno de chamada para o filho. Semelhante ao acima, a criança substitui o retorno de chamada para receber indicações dos pais.
shr
@shr alguma chance de compartilhar sua solução para passar um objeto com retorno de chamada?
Imad El Hitti
1
Essa é uma solução elegante, essa deve ser a resposta aceita, basta alterar o método de importação como import {Subject} de 'rxjs';
VIKAS KOHLI
5

Angular - Chame o método do componente filho no modelo do componente pai

Você tem ParentComponent e ChildComponent que se parecem com isso.

parent.component.html

insira a descrição da imagem aqui

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

Quando servir, fica assim:

insira a descrição da imagem aqui

Quando o usuário se concentrar no elemento de entrada ParentComponent, você deseja chamar o método doSomething () de ChildComponent.

Simplesmente faça o seguinte:

  1. Atribua um nome de variável DOM ao seletor app-child em parent.component.html (prefixo com # - hashtag) ; nesse caso, chamamos de appChild.
  2. Atribua o valor da expressão (do método que você deseja chamar) ao evento de foco do elemento de entrada.

insira a descrição da imagem aqui

O resultado:

insira a descrição da imagem aqui

Hemant Ramphul
fonte
OK, mas também queremos fazê-lo por meio de programação usando ts
canbax
Para utilização a partir de dentro do componente: @ViewChild('appChild', { static: false }) appChild: ElementRef<HTMLElement>;e uso posteriorthis.appChild.doSomething()
Gil Epshtain
4

A resposta do usuário6779899 é clara e genérica. No entanto, com base na solicitação de Imad El Hitti, uma solução leve é ​​proposta aqui. Isso pode ser usado quando um componente filho está firmemente conectado a apenas um pai.

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
fonte
2

Considere o seguinte exemplo,

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

Leia mais sobre o @ViewChild aqui.

lwairore
fonte
0

Eu tive uma situação exata em que o componente pai tinha um Selectelemento em um formulário e, ao enviar, precisava chamar o método do componente filho relevante de acordo com o valor selecionado no elemento select.

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

Espero que isto ajude.

Vibhor Dube
fonte