Dziecko nasłuchuje zdarzenia rodzica w Angular 2

82

W Angular Docs jest temat słuchania dziecięcych wydarzeń od rodziców. W porządku. Ale moim celem jest coś odwrotnego !. W mojej aplikacji jest „admin.component”, który przechowuje widok układu strony administratora (menu paska bocznego, pasek zadań, status itp.). W tym komponencie nadrzędnym skonfigurowałem system routerów do zmiany widoku głównego między innymi stronami administratora. Problem polega na zapisywaniu rzeczy po zmianie, użytkownik klika przycisk Zapisz na pasku zadań (który jest umieszczony w komponencie admin.component), a komponent potomny musi nasłuchiwać tego zdarzenia kliknięcia, aby wykonać zapisywanie personelu.

Hamed Hamedi
źródło
2
wydaje się, że najlepszą praktyką w tym celu jest użycie usługi i obserwowalne wysyłanie zdarzenia.
zpul,
Twoje pytanie nie różni się zbytnio od tego: stackoverflow.com/questions/35560860/…
freethebees
@freethebees Może rozwiązanie jest takie samo, ale problem ma inny kształt i moim zamiarem jest znalezienie najlepszego podejścia do tej sytuacji.
Hamed Hamedi
Nie będziemy konfigurować usługi dla pojedynczego zdarzenia przekazywanego do komponentu podrzędnego.
TR3B

Odpowiedzi:

101

Myślę, że ten dokument mógłby ci pomóc:

W rzeczywistości możesz wykorzystać obserwowalny / temat, który rodzic zapewnia swoim dzieciom. Coś w tym stylu:

@Component({
  (...)
  template: `
    <child [parentSubject]="parentSubject"></child>
  `,
  directives: [ ChildComponent ]
})
export class ParentComponent {
  parentSubject:Subject<any> = new Subject();

  notifyChildren() {
    this.parentSubject.next('some value');
  }
}

Komponent potomny może po prostu zasubskrybować ten temat:

@Component({
  (...)
})
export class ChildComponent {
  @Input()
  parentSubject:Subject<any>;

  ngOnInit() {
    this.parentSubject.subscribe(event => {
      // called when the notifyChildren method is
      // called in the parent component
    });
  }

  ngOnDestroy() {
    // needed if child gets re-created (eg on some model changes)
    // note that subsequent subscriptions on the same subject will fail
    // so the parent has to re-create parentSubject on changes
    this.parentSubject.unsubscribe();
  }

}

W przeciwnym razie możesz w podobny sposób wykorzystać usługę współdzieloną zawierającą taki temat ...

Thierry Templier
źródło
Wywołanie zwrotne w moim komponencie podrzędnym nie będzie działać, chyba że komponent nadrzędny rozgłasza następną wartość z poziomu ngInit. Dlaczego tak się dzieje?
cjsimon
5
Czy są jakieś zalety używania tej metody w porównaniu z ViewChild?
sunnyiitkgp
Chociaż wygląda to bardziej profesjonalnie, po kilku godzinach na eksperymentowanie w mojej sytuacji nie mogłem wyjść z kłopotów z UnsubscribeErrors po dodaniu / usunięciu elementów potomnych (a tym samym wypisaniu się z tematu). Więc dzięki TimeBoxing poszedłem do znacznie prostszej odpowiedzi @StephenPaul. W każdym razie dziękuję za odpowiedź i prawdopodobnie nie rozumiem teraz biblioteki rxjs wystarczająco dobrze, aby w pełni ją zrozumieć i wprowadzić odpowiednie (dodatkowe) poprawki do twojego przykładu, aby działał bezbłędnie.
Bernoulli IT
116

Pomyślałem, że ze względu na potomność wspomnę o bardziej konwencjonalnym rozwiązaniu tego problemu : po prostu uzyskaj odniesienie do ViewChild, a następnie wywołaj bezpośrednio jedną z jego metod.

@Component({
  selector: 'app-child'
})
export class ChildComponent {

  notifyMe() {
    console.log('Event Fired');
  }
}

@Component({
  selector: 'app-parent',
  template: `<app-child #child></app-child>`
})
export class ParentComponent {

  @ViewChild('child')
  private child: ChildComponent;

  ngOnInit() {
    this.child.notifyMe();
  }
}
Stephen Paul
źródło
3
Wywala to jak najszybciej wprowadzić składnik nadrzędny ... Cannot read property 'notifyMe' of undefined.
miły użytkownik
1
Poszedłem na to bardzo proste rozwiązanie w porównaniu z bardziej profesjonalnym rozwiązaniem @ThierryTemplier (zobacz mój komentarz tam).
Bernoulli IT
2
@BernoulliIT masz rację. Przypuszczam, że pod koniec dnia osiągają mniej więcej to samo. Moje rozwiązanie wykorzystuje mniej kodu. Jego rozwiązanie oznacza, że ​​komponent nadrzędny nie musi wstrzykiwać komponentu potomnego, aby wysyłać do niego wiadomości. RXJS jest świetny, ale często widzę nadużywanie go w projektach Angular. Ludzie myślą, że to „srebrna kula” czy coś. W rzeczywistości może to znacznie skomplikować sprawę, niż jest to konieczne. Spójrz na jego komentarz w jego ngOnDestroy()metodzie. Jeśli o mnie chodzi, to jest zbyt niejasne. To tylko błąd czekający na wystąpienie IMHO.
Stephen Paul,
1
„Spójrz na jego komentarz w jego metodzie ngOnDestroy ()”. Myślę, że właśnie z tego powodu „cierpiałem”, próbując tego podejścia, potem utknąłem i nie miałem zbyt wiele czasu na dalsze badanie (w przypadku komercyjnego projektu, nad którym pracuję). Chociaż "zraniło to trochę" moje zawodowe serce;)
Bernoulli IT
1
Dzięki @StephenPaul, dla mnie metoda Parent Calls a Viewchild była dobra, ale Twoja odpowiedź pomogła mi osiągnąć moje przeznaczenie!
MKJ
14

Jeśli dobrze zrozumiem pytanie, możliwe będzie tutaj bardziej proste podejście. Założenia -

  • OP ma przycisk zapisywania w komponencie nadrzędnym
  • Dane, które należy zapisać, znajdują się w komponentach podrzędnych
  • Wszystkie inne dane, których może potrzebować składnik podrzędny, można uzyskać z usług

W komponencie macierzystym

<button type="button" (click)="prop1=!prop1">Save Button</button>
<app-child-component [setProp]='prop1'></app-child-component>

A w dziecku ...

prop1:boolean;
  @Input()
  set setProp(p: boolean) {
    // -- perform save function here
}

To po prostu wysyła kliknięcie przycisku do komponentu podrzędnego. Stamtąd składnik potomny może niezależnie zapisywać dane.

EDYCJA: jeśli dane z szablonu nadrzędnego również muszą zostać przekazane wraz z kliknięciem przycisku, jest to również możliwe przy takim podejściu. Daj mi znać, jeśli tak jest, a zaktualizuję próbki kodu.

charsi
źródło
0

Dla tych, którzy otrzymują Cannot read property 'notifyMe' of undefined

Spróbuj wywołać metodę wewnątrz ngAfterViewInit()intead ofngOnInit()

Bazidoa
źródło