Comment émettre un événement de parent à enfant?

112

J'utilise actuellement Angular 2. Habituellement, nous utilisons @Output() addTab = new EventEmitter<any>();puis addTab.emit()pour émettre un événement au composant parent.

Y a-t-il moyen de le faire vice cersa, du parent à l'enfant?

code1
la source
2
Changez simplement la valeur d'une expression passée en entrée au composant enfant, et le composant enfant l'obtiendra (et sera informé par ngOnChanges). Vous pouvez également émettre un événement à l'aide d'un service partagé qui a un Observable.
JB Nizet

Réponses:

192

En utilisant RxJs , vous pouvez déclarer un Subjectdans votre composant parent et le passer en tant que Observablecomposant enfant, le composant enfant doit simplement s'y abonner Observable.

Parent-composant

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

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

Parent-HTML

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

Composant enfant

private eventsSubscription: Subscription;

@Input() events: Observable<void>;

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

ngOnDestroy() {
  this.eventsSubscription.unsubscribe();
}
BlueNC
la source
13
En outre, les données peuvent être transmises avec l'événement en utilisant cette approche. Comme this.eventsSubject.next({data});chez le parent, puis this.events.subscribe(({data}) => doSomething(data));chez l'enfant.
vince le
2
J'ai édité cette excellente réponse pour ajouter une désinscription. +1
Umar Farooq Khawaja
1
Probablement une question pour débutant ici: pourquoi se convertir eventsSubjecten observable au lieu de simplement s'y abonner en tant que sujet?
Justin Morgan
11
Conversion d'événementsSubject en Observable, empêche le composant enfant d'appeler next ().
BlueNC
2
Merci pour cette solution, ÇA MARCHE mais j'obtiens une erreur dans la console: "core.js: 6185 ERROR TypeError: Impossible de lire la propriété 'subscribe' de undefined" L'erreur pointe vers les events.subscribe () dans le ngOnInit du composant enfant: "this.eventsSubscription = this.events.subscribe (() => doSomething ());" J'utilise les versions: "@ angular / cli": "~ 9.1.0" et "rxjs" : "~ 6.5.4"
Eduardo
72

Autant que je sache, il existe 2 façons standard de le faire.

1. @Input

Chaque fois que les données du parent changent, l'enfant en est informé dans la méthode ngOnChanges. L'enfant peut agir en conséquence. C'est la manière standard d'interagir avec un enfant.

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. Concept de service partagé

Créer un service et utiliser une observable dans le service partagé. L'enfant y souscrit et chaque fois qu'il y a un changement, l'enfant sera averti. C'est également une méthode populaire. Lorsque vous souhaitez envoyer autre chose que les données que vous passez en entrée, cela peut être utilisé.

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
la source
J'ai essayé la première méthode et cela a bien fonctionné jusqu'à un certain point et après cela, j'ai reçu un message d'erreur indiquant que la valeur a changé maintenant, plus tôt c'est faux et maintenant c'est vrai. Cela ne m'a donné aucune autre explication en dehors de cela.
code1
2
Étant donné que vous voulez dire une ExpressionChangedAfterItHasBeenCheckedError, cette erreur ne sera pas générée en mode production.
user1337
<child [data]="inputToChild"> </child>devrait probablement être <child [data]="(inputToChild)"> </child>pour obtenir des changements
pmc
28

Dans un composant parent, vous pouvez utiliser @ViewChild () pour accéder à la méthode / variable du composant enfant.

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

Mettre à jour:

Angulaire à partir de 8 -

@ViewChild(NumberComponent, { static: false })
Hardik Patel
la source
4
Cela semble plus propre que de travailler avec des observables. L'inconvénient est que l'enfant doit être en vue. Si l'enfant est chargé avec le routage par exemple, cela échouera comme ce numberComponentsera le cas undefined.
Shy Agam
1
est-ce une bonne pratique? Je suis d'accord avec le fait que c'est plus propre que les observables, mais je doute de manipuler les variables de l'enfant du parent. Quoi qu'il en soit, cela a fonctionné si bien pour mes besoins, merci!
Takatalvi
1
C'est un bon conseil. On dirait que @ViewChild est modifié dans Angular 8, ou du moins j'ai dû spécifier les options de ViewChild. J'ai utilisé ceci dans mon cas: @ViewChild (Other, {static: false}) private otherComponent: Other;
Tore Aurstad
8

Utilisez le décorateur @Input () dans votre composant enfant pour permettre au parent de se lier à cette entrée.

Dans le composant enfant, vous le déclarez tel quel:

@Input() myInputName: myType

Pour lier une propriété du parent à un enfant, vous devez ajouter dans votre modèle les crochets de liaison et le nom de votre entrée entre eux.

Exemple :

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

Mais attention, les objets sont passés comme référence, donc si l'objet est mis à jour dans l'enfant, la var du parent sera trop mise à jour. Cela peut parfois conduire à un comportement indésirable. Avec les types principaux, la valeur est copiée.

Pour aller plus loin, lisez ceci:

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

Noki
la source
1

Dans le parent, vous pouvez référencer l'enfant en utilisant @ViewChild. Si nécessaire (c'est-à-dire lorsque l'événement serait déclenché), vous pouvez simplement exécuter une méthode dans l'enfant depuis le parent en utilisant la référence @ViewChild.

Paul Evans
la source