Rufen Sie die untergeordnete Komponentenmethode aus der übergeordneten Klasse auf - Angular

130

Ich habe eine untergeordnete Komponente erstellt, die eine Methode enthält, die ich aufrufen möchte.

Wenn ich diese Methode aufrufe, wird nur die console.log()Zeile ausgelöst, die testEigenschaft wird nicht festgelegt ?

Unten ist die Schnellstart-Angular-App mit meinen Änderungen.

Elternteil

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

Kind

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

Wie kann ich die testEigenschaft auch einstellen ?

Shammelburg
quelle
Mögliches Duplikat von Call a method der untergeordneten Komponente
Damjan Pavlica
Sie können diese Antwort hier überprüfen: stackoverflow.com/a/53057589/6663458
Muhammad Mabrouk

Antworten:

209

Sie können dies tun, indem Sie @ViewChildfür weitere Informationen diesen Link überprüfen

Mit Typenwahl

untergeordnete Komponente

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

übergeordnete Komponente

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

  @ViewChild(ChildCmp) child:ChildCmp;

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

Mit String-Selektor

untergeordnete Komponente

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

übergeordnete Komponente

@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
quelle
6
Ich habe Ihren Ansatz befolgt, aber bei der Verwendung von Anweisungen wird ein Fehler angezeigt: [ChildCmp]. Der Fehler lautet: Anweisungen 'sind im Typ' Komponente 'nicht vorhanden. Ich habe es gegoogelt und festgestellt, dass Direktiven in rc5 veraltet sind. Also, wie man mit der neueren Version umgeht. Bitte helfen Sie.
Waleed Shahzaib
1
Versuchen Sie diesen Link angle.io/guide/component-interaction und kommentieren Sie den Direktiven-Link
rashfmnb
5
Wie funktioniert es, wenn mehrere Kinder derselben Klasse anwesend sind?
Anandhu Ajayakumar
@rashfmnb "Erklärung erwartet". Beim Versuch, @ViewChild ('child') child: ChildCmp; in die Komponente zu schreiben, tritt ein Fehler auf. Bitte helfen Sie! Und auch ich kann nicht dasselbe in die Direktive importieren, es gibt mir einen Fehler wie "Direktive: (typeof EmployeeProfileC ..." ist nicht dem Parameter vom Typ 'Component' zuweisbar. Das Objektliteral kann nur bekannte Eigenschaften angeben, und 'Direktive' nicht existieren in Typ 'Komponente'. "
Trilok Pathak
1
Dies ist eine richtige Antwort, aber es entstehen eng gekoppelte Komponenten . Ein besseres Muster ist die Verwendung von InputEigenschaften: eine beobachtbare Eigenschaft, auf die das Kind reagiert, indem es seine eigene interne Funktion aufruft . Siehe die Antwort von user6779899
Bogdan D
56

Das hat bei mir funktioniert! Rufen Sie für Angular 2 die untergeordnete Komponentenmethode in der übergeordneten Komponente auf

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.
quelle
4
Was ist ChildVM in dieser Zeile: @ViewChild (ChildComponent) child: ChildVM;
Waleed Shahzaib
@WaleedShahzaib Ich denke, OP gemeint ChildComponentmitChildVM
Ajeet Shah
1
Ich dachte, dies würde eine separate Instanz der Komponente erstellen, aber es ruft tatsächlich die Funktion Ihrer Instanz mit ihren Variablen im aktuellen Status dieser Komponente auf, heilige Kuh! Diese Methode ist viel besser als die erste Antwort!
Tatsu
3
Ich bekomme immer einen undefinierten Wert von "this.child"
Ambuj Khanna
2
Ich vermute, dass 'this.child' undefiniert ist, dass ViewChild entweder auf etwas zeigt, das in der Vorlage nicht vorhanden ist, oder Sie versuchen, zu früh im Lebenszyklus darauf zuzugreifen, z. B. im Konstruktor.
Tony
34

Ich denke, der einfachste Weg ist die Verwendung von Betreff. Im folgenden Beispielcode wird das Kind bei jedem Aufruf von 'tellChild' benachrichtigt.

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

Arbeitsprobe auf Stackblitz

user6779899
quelle
4
Es ist eine elegante Lösung, funktioniert jedoch nicht in allen Fällen ordnungsgemäß, wahrscheinlich weil die Erkennung von Winkeländerungen beim Abonnieren nicht funktioniert.
Alexei
1
Ich fand, dass dies die beste Lösung für meinen Anwendungsfall ist. Klappt wunderbar. Vielen Dank!
Weston
Ordentlich ! In einfacheren Fällen können Sie den Betreff / Abonnement-Overhead vermeiden, indem Sie ein Objekt mit einer Rückrufmethode an das untergeordnete Element übergeben. Ähnlich wie oben überschreibt das Kind den Rückruf, um Anzeigen vom Elternteil zu erhalten.
Shr
@shr Gibt es eine Chance, dass Sie Ihre Lösung teilen können, um ein Objekt mit Rückruf zu übergeben?
Imad El Hitti
1
Dies ist eine elegante Lösung. Dies sollte die akzeptierte Antwort sein. Ändern Sie einfach die Importmethode wie import {Subject} from 'rxjs'.
VIKAS KOHLI
5

Winkel - Rufen Sie die Methode der untergeordneten Komponente in der Vorlage der übergeordneten Komponente auf

Sie haben ParentComponent und ChildComponent, die so aussehen.

parent.component.html

Geben Sie hier die Bildbeschreibung ein

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

Beim Servieren sieht es so aus:

Geben Sie hier die Bildbeschreibung ein

Wenn sich der Benutzer auf das Eingabeelement von ParentComponent konzentriert, möchten Sie die doSomething () -Methode von ChildComponent aufrufen.

Mach das einfach:

  1. Geben Sie dem App-Child-Selektor in parent.component.html einen DOM-Variablennamen (Präfix mit # - Hashtag) . In diesem Fall nennen wir ihn appChild.
  2. Weisen Sie dem Fokusereignis des Eingabeelements einen Ausdruckswert (der Methode, die Sie aufrufen möchten) zu.

Geben Sie hier die Bildbeschreibung ein

Das Ergebnis:

Geben Sie hier die Bildbeschreibung ein

Hemant Ramphul
quelle
OK, aber wir wollen es auch programmgesteuert mit ts
canbax
Zur Verwendung innerhalb der Komponente: @ViewChild('appChild', { static: false }) appChild: ElementRef<HTMLElement>;und später zur Verwendungthis.appChild.doSomething()
Gil Epshtain
4

Die Antwort von user6779899 ist ordentlich und allgemeiner. Auf der Grundlage der Anfrage von Imad El Hitti wird hier jedoch eine leichte Lösung vorgeschlagen. Dies kann verwendet werden, wenn eine untergeordnete Komponente nur mit einem übergeordneten Element eng verbunden ist.

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

Betrachten Sie das folgende Beispiel:

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

Lesen Sie hier mehr über @ViewChild .

lwairore
quelle
0

Ich hatte eine genaue Situation, in der die übergeordnete Komponente ein SelectElement in einem Formular hatte, und beim Senden musste ich die Methode der entsprechenden untergeordneten Komponente gemäß dem ausgewählten Wert aus dem ausgewählten Element aufrufen.

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

Hoffe das hilft.

Vibhor Dube
quelle