Wywołaj metodę składnika potomnego z klasy nadrzędnej - Angular

130

Utworzyłem komponent potomny, który ma metodę, którą chcę wywołać.

Kiedy wywołuję tę metodę, to tylko odpala console.log()linię, nie ustawi testwłaściwości ??

Poniżej znajduje się szybki start aplikacji Angular z moimi zmianami.

Rodzic

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

Dziecko

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

Jak mogę również ustawić testwłaściwość?

shammelburg
źródło
Możliwy duplikat wywołania metody komponentu potomnego
Damjan Pavlica
Możesz sprawdzić tę odpowiedź tutaj: stackoverflow.com/a/53057589/6663458
Muhammad Mabrouk

Odpowiedzi:

210

Możesz to zrobić, używając, @ViewChildaby uzyskać więcej informacji, sprawdź ten link

Z selektorem typu

składnik podrzędny

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

składnik macierzysty

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

  @ViewChild(ChildCmp) child:ChildCmp;

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

Z selektorem ciągów

składnik podrzędny

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

składnik macierzysty

@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
źródło
6
Podążyłem za Twoim podejściem, ale otrzymuję błąd podczas używania dyrektyw: [ChildCmp]. Błąd mówi: dyrektywy „nie istnieją w typie„ Component ”. Wyszukałem w Google i stwierdziłem, że dyrektywy są przestarzałe w rc5. Więc jak sobie z tym poradzić w nowszej wersji. Proszę pomóż.
Waleed Shahzaib
1
wypróbuj ten link angular.io/guide/component-interaction i skomentuj link do dyrektyw
rashfmnb
5
Jak sprawić, by działało, gdy jest wiele dzieci tej samej klasy?
Anandhu Ajayakumar
@rashfmnb "Oczekiwano deklaracji". Pojawia się błąd, gdy próbuję napisać @ViewChild ('child') child: ChildCmp; w komponencie. Proszę pomóż! Nie mogę też zaimportować tego samego do dyrektywy, co powoduje błąd, taki jak „dyrektywa: (typ profilu pracownika ...” nie jest przypisany do parametru typu „składnik”. Literał obiektu może określać tylko znane właściwości, a „dyrektywa” nie istnieją w typie „Component”. ”
Trilok Pathak
1
To poprawna odpowiedź, ale daje to ściśle połączone elementy . Lepszy wzór jest do użyciaInput właściwości: obserwowalnych, na które dziecko reaguje, wywołując własną funkcję wewnętrzną. Zobacz odpowiedź użytkownika6779899
Bogdan D
56

To zadziałało dla mnie! Dla Angular 2, wywołaj metodę komponentu potomnego w komponencie nadrzędnym

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
źródło
4
Co to jest ChildVM w tym wierszu: @ViewChild (ChildComponent) child: ChildVM;
Waleed Shahzaib
@WaleedShahzaib myślę OP oznaczało ChildComponentprzezChildVM
Ajeet Shah
1
Myślałem, że to stworzy oddzielną instancję komponentu, ale w rzeczywistości wywołuje funkcję z twojej instancji z jej zmiennymi w obecnym stanie tego komponentu, święta krowa! ta metoda jest o wiele lepsza niż pierwsza odpowiedź!
tatsu
3
Zawsze otrzymuję Undefined wartość „this.child”
Ambuj Khanna,
2
Domyślam się, że „this.child” jest niezdefiniowane, że albo ViewChild wskazuje na coś, czego nie ma w szablonie, albo próbujesz uzyskać do niego dostęp na zbyt wczesnym etapie cyklu życia, np. W konstruktorze.
tony
34

Myślę, że najłatwiejszym sposobem jest użycie tematu. W poniższym kodzie przykładowym dziecko zostanie powiadomione za każdym razem, gdy zostanie wywołane polecenie „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);
  });
}

Próbka robocza na Stackblitz

user6779899
źródło
4
Jest to eleganckie rozwiązanie, jednak nie we wszystkich przypadkach działa poprawnie, prawdopodobnie dlatego, że wykrywanie zmian kątowych nie działa po zasubskrybowaniu.
Alexei,
1
Okazało się, że jest to najlepsze rozwiązanie dla mojego przypadku użycia. Działa jak marzenie. Dzięki!
Weston
Schludnie! W prostszych przypadkach można uniknąć narzutu Subject / Subscribe, przekazując obiekt, który ma metodę wywołania zwrotnego do elementu podrzędnego. Podobnie jak powyżej, dziecko zastępuje wywołanie zwrotne, aby otrzymać wskazówki od rodzica.
środa
@shr jest szansa, że ​​możesz udostępnić swoje rozwiązanie, aby przekazać obiekt z wywołaniem zwrotnym?
Imad El Hitti
1
To eleganckie rozwiązanie, to powinna być akceptowana odpowiedź, po prostu zmień metodę importu, na przykład import {Temat} z 'rxjs';
VIKAS KOHLI
5

Angular - Wywołaj metodę komponentu podrzędnego w szablonie komponentu nadrzędnego

Masz ParentComponent i ChildComponent, które wyglądają tak.

parent.component.html

wprowadź opis obrazu tutaj

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

Podczas serwowania wygląda to tak:

wprowadź opis obrazu tutaj

Gdy użytkownik koncentruje się na elemencie wejściowym ParentComponent, chcesz wywołać metodę doSomething () klasy ChildComponent.

Po prostu zrób to:

  1. Nadaj selektorowi elementu podrzędnego aplikacji w parent.component.html nazwę zmiennej DOM (prefiks z # - hashtag) , w tym przypadku nazywamy to appChild.
  2. Przypisz wartość wyrażenia (metody, którą chcesz wywołać) do zdarzenia focus elementu input.

wprowadź opis obrazu tutaj

Wynik:

wprowadź opis obrazu tutaj

Hemant Ramphul
źródło
OK, ale chcemy również zrobić to programowo za pomocą ts
canbax.
Do użytku wewnątrz komponentu: @ViewChild('appChild', { static: false }) appChild: ElementRef<HTMLElement>;i później do użytkuthis.appChild.doSomething()
Gil Epshtain
4

Odpowiedź użytkownika6779899 jest zgrabna i bardziej ogólna Jednak w oparciu o prośbę Imada El Hitti zaproponowano tutaj lekkie rozwiązanie. Można tego użyć, gdy komponent podrzędny jest ściśle połączony tylko z jednym rodzicem.

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
źródło
2

Rozważmy następujący przykład:

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

Przeczytaj więcej o @ViewChild tutaj.

lwairore
źródło
0

Miałem dokładną sytuację, w której komponent nadrzędny miał Selectelement w formularzu i po przesłaniu musiałem wywołać odpowiednią metodę komponentu Child-Component zgodnie z wybraną wartością z elementu 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'];
}

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

Mam nadzieję że to pomoże.

Vibhor Dube
źródło