Jak wysłać wydarzenie z rodzica na dziecko?

112

Obecnie używam Angular 2. Zwykle używamy, @Output() addTab = new EventEmitter<any>();a następnie addTab.emit()emitujemy zdarzenie do komponentu nadrzędnego.

Czy jest jakiś sposób, żebyśmy mogli to zrobić vice cersa, od rodzica do dziecka?

kod1
źródło
2
Po prostu zmień wartość wyrażenia przekazanego jako dane wejściowe do składnika podrzędnego, a składnik podrzędny go otrzyma (i zostanie o tym poinformowany przez ngOnChanges). Możesz również wyemitować zdarzenie przy użyciu udostępnionej usługi, która ma Observable.
JB Nizet

Odpowiedzi:

193

Używając RxJs , możesz zadeklarować a Subjectw komponencie nadrzędnym i przekazać go jako Observablekomponent potomny, komponent potomny po prostu musi to zasubskrybować Observable.

Element nadrzędny

eventsSubject: Subject<void> = new Subject<void>();

emitEventToChild() {
  this.eventsSubject.next();
}

Nadrzędny HTML

<child [events]="eventsSubject.asObservable()"> </child>    

Komponent potomny

private eventsSubscription: Subscription;

@Input() events: Observable<void>;

ngOnInit(){
  this.eventsSubscription = this.events.subscribe(() => doSomething());
}

ngOnDestroy() {
  this.eventsSubscription.unsubscribe();
}
BlueNC
źródło
13
Ponadto dane mogą być przekazywane wraz ze zdarzeniem przy użyciu tego podejścia. Jak this.eventsSubject.next({data});u rodzica, potem this.events.subscribe(({data}) => doSomething(data));w dziecku.
vince
2
Zredagowałem tę świetną odpowiedź, aby dodać rezygnację z subskrypcji. +1
Umar Farooq Khawaja
1
Prawdopodobnie jest to pytanie dla początkujących: po co konwertować eventsSubjectna Observable zamiast po prostu subskrybować go jako podmiot?
Justin Morgan
11
Konwertowanie zdarzeńSubject na Observable, zapobiega wywoływaniu przez składnik potomny funkcji next ().
BlueNC
2
Dzięki za to rozwiązanie, DZIAŁA, ale w konsoli pojawia się błąd: "core.js: 6185 ERROR TypeError: Cannot read property 'subscribe' of undefined" Błąd wskazuje na zdarzenia events.subscribe () w ngOnInit komponentu podrzędnego: "this.eventsSubscription = this.events.subscribe (() => doSomething ());" Używam wersji: "@ angular / cli": "~ 9.1.0" i "rxjs" : „~ 6.5.4”
Eduardo
72

O ile wiem, możesz to zrobić na 2 standardowe sposoby.

1. @Input

Za każdym razem, gdy zmieniają się dane w rodzicu, dziecko jest o tym powiadamiane w metodzie ngOnChanges. Dziecko może na to działać. To jest standardowy sposób interakcji z dzieckiem.

Parent-Component
public inputToChild: Object;

Parent-HTML
<child [data]="inputToChild"> </child>       

Child-Component: @Input() data;

ngOnChanges(changes: { [property: string]: SimpleChange }){
   // Extract changes to the input property by its name
   let change: SimpleChange = changes['data']; 
// Whenever the data in the parent changes, this method gets triggered. You 
// can act on the changes here. You will have both the previous value and the 
// current value here.
}
  1. Koncepcja usług wspólnych

Tworzenie usługi i używanie obserwowalnego w usłudze wspólnej. Dziecko subskrybuje ją, a gdy zajdzie zmiana, zostanie o tym powiadomione. Jest to również popularna metoda. Jeśli chcesz wysłać coś innego niż dane, które przekazujesz jako dane wejściowe, możesz tego użyć.

SharedService
subject: Subject<Object>;

Parent-Component
constructor(sharedService: SharedService)
this.sharedService.subject.next(data);

Child-Component
constructor(sharedService: SharedService)
this.sharedService.subject.subscribe((data)=>{

// Whenever the parent emits using the next method, you can receive the data 
in here and act on it.})
prajwal gonsalves
źródło
Wypróbowałem pierwszą metodę i działała dobrze do pewnego momentu, a potem otrzymałem komunikat o błędzie z informacją, że wartość została teraz zmieniona, wcześniej jest fałszywa, a teraz jest prawdziwa. Poza tym nie dało mi to żadnych dalszych wyjaśnień.
code1
2
Biorąc pod uwagę, że masz na myśli ExpressionChangedAfterItHasBeenCheckedError, ten błąd nie zostanie zgłoszony w trybie produkcyjnym.
user1337
<child [data]="inputToChild"> </child>prawdopodobnie powinno być, <child [data]="(inputToChild)"> </child>aby uzyskać zmiany
pmc
28

W komponencie nadrzędnym możesz użyć @ViewChild (), aby uzyskać dostęp do metody / zmiennej komponentu potomnego.

@Component({
  selector: 'app-number-parent',
  templateUrl: './number-parent.component.html'
})
export class NumberParentComponent {
    @ViewChild(NumberComponent)
    private numberComponent: NumberComponent;
    increase() {
       this.numberComponent.increaseByOne();
    }
    decrease() {
       this.numberComponent.decreaseByOne();
    }
} 

Aktualizacja:

Kątowy od 8 wzwyż -

@ViewChild(NumberComponent, { static: false })
Hardik Patel
źródło
4
Wydaje się to czystsze niż praca z obserwabli. Wadą jest to, że dziecko musi być widoczne. Jeśli dziecko jest ładowany z routingu na przykład, to nie tak numberComponentbędzie undefined.
Shy Agam
1
czy to dobra praktyka? Zgadzam się z faktem, że jest to czystsze od obserwabli, ale wątpię w manipulowanie zmiennymi dziecka od rodzica. W każdym razie działało tak dobrze na moje potrzeby, dzięki!
Takatalvi
1
To dobra wskazówka. Wygląda na to, że @ViewChild zostało zmienione w Angular 8, albo przynajmniej musiałem określić opcje ViewChild. Użyłem tego w moim przypadku: @ViewChild (Other, {static: false}) private otherComponent: Other;
Tore Aurstad,
8

Użyj dekoratora @Input () w komponencie podrzędnym, aby umożliwić rodzicowi powiązanie z tym wejściem.

W komponencie potomnym deklarujesz, jak jest:

@Input() myInputName: myType

Aby powiązać właściwość między rodzicem a dzieckiem, musisz dodać w szablonie nawiasy wiążące i nazwę wejścia między nimi.

Przykład:

<my-child-component [myChildInputName]="myParentVar"></my-child-component>

Ale uwaga, obiekty są przekazywane jako referencje, więc jeśli obiekt jest aktualizowany w dziecku, zmienna rodzica będzie zbyt zaktualizowana. Może to czasem prowadzić do niechcianego zachowania. W przypadku typów podstawowych wartość jest kopiowana.

Aby przejść dalej, przeczytaj to:

Dokumenty: https://angular.io/docs/ts/latest/cookbook/component-communication.html

Noki
źródło
1

W ramach elementu nadrzędnego możesz odwoływać się do dziecka za pomocą @ViewChild. W razie potrzeby (tj. Gdy zdarzenie zostanie wyzwolone), możesz po prostu wykonać metodę w dziecku z rodzica, używając odwołania @ViewChild.

Paul Evans
źródło