Come trasmettere un evento da genitore a figlio?


111

Attualmente sto usando Angular 2. Di solito usiamo @Output() addTab = new EventEmitter<any>();e poi addTab.emit()per emettere un evento al componente genitore.

C'è un modo per farlo vice cersa, da genitore a figlio?


2
Basta cambiare il valore di un'espressione passata come Input al componente figlio e il componente figlio lo riceverà (e sarà informato da ngOnChanges). Puoi anche emettere un evento utilizzando un servizio condiviso che ha un osservabile.
JB Nizet

Risposte:


192

Usando RxJs , puoi dichiarare a Subjectnel tuo componente genitore e passarlo come Observablecomponente figlio, il componente figlio deve solo iscriverti a questo Observable.

Parent-Componente

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

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

Parent-HTML

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

Child-Componente

private eventsSubscription: Subscription;

@Input() events: Observable<void>;

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

ngOnDestroy() {
  this.eventsSubscription.unsubscribe();
}

13
Inoltre, i dati possono essere trasmessi insieme all'evento utilizzando questo approccio. Come, this.eventsSubject.next({data});nel genitore, poi this.events.subscribe(({data}) => doSomething(data));nel bambino.
Vince

2
Ho modificato questa ottima risposta per aggiungere l'annullamento dell'iscrizione. +1
Umar Farooq Khawaja

1
Probabilmente una domanda per principianti qui: perché convertirsi eventsSubjectin un osservabile invece di abbonarsi ad esso come soggetto?
Justin Morgan

11
Conversione di eventi Soggetto a un osservabile, impedisce al componente figlio di chiamare next ().
BlueNC

2
Grazie per questa soluzione, FUNZIONA ma ricevo un errore nella console: "core.js: 6185 ERRORE TypeError: Impossibile leggere la proprietà 'subscribe' di undefined" L'errore punta a events.subscribe () in ngOnInit del componente figlio: "this.eventsSubscription = this.events.subscribe (() => doSomething ());" Sto usando le versioni: "@ angular / cli": "~ 9.1.0" e "rxjs" : "~ 6.5.4"
Eduardo

72

Per quanto ne so, ci sono 2 modi standard per farlo.

1. @Input

Ogni volta che i dati nel genitore cambiano, il bambino ne viene informato nel metodo ngOnChanges. Il bambino può agire di conseguenza. Questo è il modo standard di interagire con un bambino.

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. Concetto di servizio condiviso

Creazione di un servizio e utilizzo di un osservabile nel servizio condiviso. Il bambino si iscrive e ogni volta che c'è un cambiamento, il bambino verrà avvisato. Questo è anche un metodo popolare. Quando vuoi inviare qualcosa di diverso dai dati che passi come input, questo può essere usato.

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

Ho provato il primo metodo e ha funzionato bene fino a un certo punto, dopodiché ho ricevuto un messaggio di errore che indicava che il valore è stato cambiato ora, prima è falso e ora è vero. Non mi ha dato ulteriori spiegazioni a parte questo.
code1

2
Dato che intendi un ExpressionChangedAfterItHasBeenCheckedError, questo errore non verrà generato in modalità di produzione.
user1337

<child [data]="inputToChild"> </child>probabilmente dovrebbe essere <child [data]="(inputToChild)"> </child>quello di ottenere modifiche
pmc

28

In un componente padre è possibile utilizzare @ViewChild () per accedere al metodo / variabile del componente figlio.

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

Aggiornare:

Angolare 8 in poi -

@ViewChild(NumberComponent, { static: false })

4
Sembra più pulito che lavorare con gli osservabili. Lo svantaggio è che il bambino deve essere in vista. Se il bambino viene caricato con il routing, ad esempio, questo fallirà come numberComponentsarà undefined.
Shy Agam

1
è una buona pratica? Sono d'accordo con il fatto che sia più pulito degli osservabili, ma dubito di manipolare le variabili del bambino dal genitore. Comunque, ha funzionato così bene per le mie esigenze, grazie!
Takatalvi

1
Questo è un buon suggerimento. Sembra che @ViewChild sia cambiato in Angular 8, o almeno ho dovuto specificare le opzioni di ViewChild. Ho usato questo nel mio caso: @ViewChild (Other, {static: false}) private otherComponent: Other;
Tore Aurstad

8

Utilizzare il decoratore @Input () nel componente figlio per consentire al genitore di collegarsi a questo input.

Nel componente figlio lo dichiari così com'è:

@Input() myInputName: myType

Per legare una proprietà da genitore a figlio devi aggiungere nel tuo template le parentesi di legame e il nome del tuo input tra di loro.

Esempio :

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

Ma attenzione, gli oggetti vengono passati come riferimento, quindi se l'oggetto viene aggiornato nel figlio, la variabile del genitore sarà troppo aggiornata. Questo potrebbe portare a comportamenti indesiderati a volte. Con i tipi primari il valore viene copiato.

Per andare oltre, leggi questo:

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


1

All'interno del genitore, puoi fare riferimento al bambino utilizzando @ViewChild. Quando necessario (cioè quando l'evento verrebbe attivato), puoi semplicemente eseguire un metodo nel figlio dal genitore usando il riferimento @ViewChild.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.