web-dev-qa-db-fra.com

Comment injecter un composant parent dans un composant enfant?

J'essaie d'injecter un composant parent dans un composant enfant. Je pensais que cela serait simple - spécifiez/injectez simplement le composant parent dans la constructor() de l'enfant:

constructor(private _parent:AppComponent) {}   // child component constructor

Je reçois l'erreur suivante:

EXCEPTION: Impossible de résoudre tous les paramètres pour ChildComponent (?). Assurez-vous qu'ils ont tous un type ou des annotations valides.

Qu'est-ce que je rate?

ChildComponent:

import {Component} from 'angular2/core';
import {AppComponent} from './app.component';

@Component({
  selector: 'child',
  template: `<p>child</p>`
})
export class ChildComponent {
  constructor(private _parent:AppComponent) {}
}

AppComponent:

import {Component} from 'angular2/core';
import {ChildComponent} from './child.component';

@Component({
  selector: 'my-app',
  template: `{{title}} <child></child>
  `,
  directives: [ChildComponent]
})
export class AppComponent {
  title = "Angular 2 - inject parent";
  constructor() { console.clear(); }
}

Plunker

30
Mark Rajcok

Voir @ EricMartinez's comment pour la réponse. Le problème semble être une référence circulaire lorsque A importe B et B importe A.

Voici un plunker qui utilise deux fichiers au lieu du fichier qui se trouve dans le plunker d'Eric .

Le seul changement par rapport à mon plunker original est dans le ChildComponent:

import {Component, Inject, forwardRef} from 'angular2/core';
....
constructor(@Inject(forwardRef(() => AppComponent)) private _parent:AppComponent)

Je ne sais pas avec certitude si cela élimine la référence circulaire, étant donné que A et B s’importent toujours, mais cela semble fonctionner.

Voir aussi https://github.com/angular/angular/issues/3216 , où Miško déclare:

Cette [déclaration non conviviale utilisant forwardRef ()] est une limitation de JS et de la manière dont les déclarations de fonction sont levées. Chaque fois que vous avez une dépendance circulaire, vous aurez besoin de forwardRef :-( Je ne vois tout simplement pas s’en éloigner.

Je dirais que vous ne devriez pas être dans une situation où votre parent a besoin de savoir sur les enfants et les enfants doivent savoir sur les parents. @Query devrait prendre en charge la plupart des cas d'utilisation.

Je suis désolé, mais je suis d’accord sur le fait que c’est une douleur dans de rares cas, mais je ne vois pas de moyen d’en sortir, et par conséquent, le problème ne peut pas faire l’objet d’une action, il sera fermé.

Hmm ... la raison pour laquelle j'ai essayé d'injecter le parent était parce que je voyais deux façons pour un enfant de communiquer avec un parent:

  1. l'enfant définit les propriétés de sortie et émet des événements auxquels le parent s'abonne
  2. l'enfant injecte le parent (par exemple, Pane peut injecter des onglets) et peut ensuite appeler des méthodes sur le parent

Et j'essayais de déterminer quand utiliser chaque approche. Miško donne l'impression que 2. devrait être rare.

Mise à jour: J'y pensais un peu plus ... 1. C'est mieux parce qu'il y a moins de couplage entre l'enfant et le parent. Avec 1. l'enfant n'a pas besoin de connaître (et ne devrait probablement pas connaître) l'API/interface publique du parent. 
Dans le sens inverse (par exemple, le parent utilise @ViewChild (@Query est maintenant déconseillé) pour obtenir une référence à l'enfant, puis appelle des méthodes sur l'enfant), le couplage est correct, car le parent utilise le composant enfant. connaître l’API/interface publique de l’enfant, c’est-à-dire les propriétés et méthodes publiques d’entrée et de sortie.

41
Mark Rajcok

vous pouvez simplement utiliser le décorateur @Host comme ceci:

import {Component, Host} from 'angular2/core';
....
constructor(@Host() private app: AppComponent)
4
itay oded