Вызов метода дочернего компонента из родительского класса - Angular


130

Я создал дочерний компонент, у которого есть метод, который я хочу вызвать.

Когда я вызываю этот метод, он только запускает console.log()строку, он не устанавливает testсвойство ??

Ниже приведено приложение Angular для быстрого старта с моими изменениями.

родитель

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

ребенок

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

Как я могу также установить testсвойство?


Возможный дубликат метода вызова дочернего компонента
Damjan Pavlica

Вы можете проверить этот ответ здесь: stackoverflow.com/a/53057589/6663458
Мухаммад Мабрук

Ответы:


210

Вы можете сделать это, используя @ViewChildдля получения дополнительной информации эту ссылку

С селектором типа

дочерний компонент

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

родительский компонент

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

  @ViewChild(ChildCmp) child:ChildCmp;

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

С селектором строк

дочерний компонент

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

родительский компонент

@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
Я последовал вашему подходу, но получаю сообщение об ошибке при использовании директив: [ChildCmp], ошибка говорит: директивы «не существует в типе« Компонент ». Я погуглил и обнаружил, что директивы устарели в rc5. Итак, как с этим справиться в более новой версии. Пожалуйста помоги.
Валид Шахзаиб

1
попробуйте эту ссылку angular.io/guide/component-interaction и прокомментируйте ссылку на директивы
rashfmnb

5
Как заставить его работать, когда есть несколько детей одного класса ??
Анандху Аджаякумар

@rashfmnb "Ожидается объявление". При попытке написать @ViewChild ('child') child: ChildCmp; в компоненте возникает ошибка. Пожалуйста помоги! И также я не могу импортировать то же самое в директиву, это дает мне ошибку, например, директива: (typeof EmployeeProfileC ... 'не может быть назначен параметру типа' Component '. Литерал объекта может указывать только известные свойства, а' директива 'не существуют в типе 'Компонент'. "
Trilok Pathak

1
Это правильный ответ, но он дает сильно связанные компоненты . Лучшим шаблоном является использование Inputсвойств: наблюдаемого, на которое дочерний элемент реагирует, вызывая свою собственную внутреннюю функцию. См. Ответ user6779899
Bogdan D

56

Это сработало для меня! Для Angular 2 вызовите метод дочернего компонента в родительском компоненте

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
Что такое ChildVM в этой строке: @ViewChild (ChildComponent) child: ChildVM;
Waleed Shahzaib

@WaleedShahzaib Я думаю , что ОП означало ChildComponentпоChildVM
Ajeet Shah

1
Я думал, что это создаст отдельный экземпляр компонента, но на самом деле он вызывает функцию из вашего экземпляра с ее переменными в текущем состоянии этого компонента, святая корова! этот метод намного лучше, чем первый ответ!
tatsu

3
Я всегда получаю неопределенное значение this.child
Амбудж Кханна

2
Я предполагаю, что значение this.child не определено: либо ViewChild указывает на то, чего нет в шаблоне, либо вы пытаетесь получить к нему доступ слишком рано в жизненном цикле, например, в конструкторе.
Тони

34

Я думаю, что самый простой способ - это использовать тему. В приведенном ниже примере кода ребенок будет уведомляться каждый раз, когда вызывается 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);
  });
}

Рабочий образец на Stackblitz


4
Это элегантное решение, однако оно не работает должным образом во всех случаях, вероятно, из-за того, что обнаружение изменений Angular не работает из подписки.
Alexei

1
Считаю, что это лучшее решение для моего случая использования. Работает как шарм. Спасибо!
Уэстон

Аккуратно! В более простых случаях вы можете избежать накладных расходов Subject / Subscribe, передав объект, имеющий метод обратного вызова, дочернему элементу. Как и в предыдущем случае, дочерний элемент переопределяет обратный вызов для получения указаний от родителя.
shr

@shr есть ли шанс, что вы можете поделиться своим решением для передачи объекта с обратным вызовом?
Имад Эль Хитти

1
Это элегантное решение, это должен быть принятый ответ, просто измените метод импорта, например import {Subject} из 'rxjs';
VIKAS KOHLI

5

Angular - вызов метода дочернего компонента в шаблоне родительского компонента

У вас есть ParentComponent и ChildComponent, которые выглядят следующим образом.

parent.component.html

введите описание изображения здесь

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

При подаче это выглядит так:

введите описание изображения здесь

Когда пользователь фокусируется на элементе ввода ParentComponent, вы хотите вызвать метод doSomething () ChildComponent.

Просто сделайте это:

  1. Дайте селектору приложений-потомков в parent.component.html имя переменной DOM (префикс с # - хэштегом) , в этом случае мы называем его appChild.
  2. Присвойте значение выражения (метода, который вы хотите вызвать) событию фокуса входного элемента.

введите описание изображения здесь

Результат:

введите описание изображения здесь


Хорошо, но мы также хотим сделать это программно, используя ts
canbax

Для использования внутри компонента: @ViewChild('appChild', { static: false }) appChild: ElementRef<HTMLElement>;и для последующего использованияthis.appChild.doSomething()
Гил Эпштейн

4

Ответ user6779899 изящный и более общий. Однако, по запросу Имада Эль Хитти, здесь предлагается облегченное решение. Это можно использовать, когда дочерний компонент тесно связан только с одним родителем.

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

Рассмотрим следующий пример,

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

Подробнее о @ViewChild здесь.


0

У меня была точная ситуация, когда родительский компонент имел Select элемент в форме, и при отправке мне нужно было вызвать соответствующий метод дочернего компонента в соответствии с выбранным значением из элемента 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(){ }
}

Надеюсь это поможет.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.