Виклик дочірнього компонентного методу з батьківського класу - 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властивість?



Ви можете перевірити цю відповідь тут: stackoverflow.com/a/53057589/6663458
Muhammad Mabrouk

Відповіді:


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') дитина: ChildCmp; Будь ласка, допоможіть! А також я не можу імпортувати те саме в директиві, це дає мені помилку типу "директива: (typeof EmployeeProfileC ..." не призначається параметру типу "Компонент". Літерал об'єкта може вказувати лише відомі властивості, а "директива" не існують у типі "Компонент". "
Трилок Патхак

1
Це правильна відповідь, але вона створює щільно з'єднані компоненти . Краща модель - використовувати Inputвластивості: спостереження, на яке дитина реагує, називаючи власну внутрішню функцію. Дивіться відповідь користувача6779899
Богдан Д

56

Це працювало для мене! Для кутових 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) дитина: ChildVM;
Уелід Шахзайб

@WaleedShahzaib Я думаю, що ОП мається ChildComponentна увазіChildVM
Ajeet Shah

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

3
Я завжди отримую не визначене значення "this.child"
Ambuj Khanna

2
Моя здогадка щодо невизначеності "this.child" полягає в тому, що або ViewChild вказує на щось, що не існує в шаблоні, або ви намагаєтесь отримати доступ до нього занадто рано в життєвому циклі, наприклад, в конструкторі.
тоні

34

Я думаю, що найпростішим способом є використання Subject. У нижченаведеному прикладі коду дитина буде отримувати повідомлення щоразу, коли викликається "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
Це елегантне рішення, однак воно не працює належним чином у всіх випадках, можливо, завдяки виявленню кутових змін не працює підписка.
Олексій

1
Це було найкращим рішенням для мого використання. Працює як шарм. Дякую!
Вестон

Акуратно! У більш простих випадках ви можете уникнути накладних витрат суб'єкта / підписки, передавши дитині об'єкт, який має метод зворотного виклику. Як і у вищезгаданому, дитина перекриває зворотний дзвінок, щоб отримувати показання від батьків.
shr

@shr будь-який шанс ви можете поділитися своїм рішенням для передачі об'єкта із зворотним дзвінком?
Імад Ель

1
Це елегантне рішення, це має бути прийнята відповідь, просто змініть метод імпорту, як імпорт {Subject} з 'rxjs';
VIKAS KOHLI

5

Кутовий - метод дочірнього компонента виклику в шаблоні батьківського компонента

У вас є 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

Відповідь користувача6779899 є акуратною та загальнішою. ​​Однак, виходячи із запиту Імада Ель Хітті, тут пропонується легке рішення. Це можна використовувати, коли дочірній компонент тісно пов'язаний лише з одним з батьків.

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елемент у формі та під час подання, мені потрібно було викликати відповідний метод дочірнього компонента відповідно до вибраного значення з елемента вибору.

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.