J'essaie d'accéder à un composant qui a été créé à l'aide d'un sélecteur dans du code HTML. J'avais l'impression (à tort, semble-t-il) que les fournisseurs chercheraient une instance existante et la fourniraient lorsqu'ils seraient injectés dans un autre composant, mais je comprends manifestement mal le processus de création hiérarchique des fournisseurs.

Dans ce qui suit, j'ai un composant, qui utilise un sélecteur dans son modèle HTML pour créer une instance de TopLevelComponent.

J'essaie d'accéder à ce TopLevelComponent en créant un fournisseur et en utilisant DI pour pousser cette instance via le constructeur.

(Toutes mes excuses si ce code n'est pas parfait, je viens de mettre en place un exemple rapide.)

@component({
    selector: 'my-app',
    template: '<top-level-component></top-level-component>',

    directives: [TopLevelComponent],
    providers: [TopLevelComponent],
})
export class MyApp {

    constructor( private topLevelComponent: TopLevelComponent) {

    }
}

@component({
    selector: 'top-level-component',
    template: '',
})
export class TopLevelComponent {

    constructor() {
        console.log('CONSTRUCTED A TopLevelComponent...');
    }

}

Mais, au lieu de faire passer une instance de TopLevelComponent à mon composant, j'en reçois deux (comme en témoignent les deux journaux de "CONSTRUCTED A TopLevelComponent ..." présents dans le journal de débogage).

Si je supprime «private topLevelComponent: TopLevelComponent» du constructeur, je n'obtiens qu'une seule instance du composant, mais je ne semble pas pouvoir m'en procurer.

J'ai donc deux questions

  • Comment utiliser DI pour passer l'instance du composant créé par le sélecteur au constructeur d'autres composants

Ou

  • Si je n'inclus pas <top-level-component></top-level-component> dans le code HTML de l'application, comment puis-je injecter ce code HTML dans l'application pour qu'il s'affiche correctement?

Et un troisième

  • Si les deux éléments ci-dessus sont possibles, quelle est la méthode recommandée?

Je pensais que @ViewChild serait la bonne méthode, car il n'y aura (!) Qu'une seule instance de ce composant, mais comme il est injecté dans un certain nombre de composants différents, je ne pense pas que ce soit possible.

0
Lee Winder 8 août 2016 à 20:18

3 réponses

Meilleure réponse

Si vous ajoutez un composant à providers: [...], alors le composant est traité comme une classe ordinaire. Si DI trouve un tel fournisseur lorsque TopLevelComponent est demandé, il crée une instance du décorateur de classe de composant @Component(...) qui est ignoré.

Si un composant est répertorié dans directives: [...] DI les trouvera comme composants et directives s'ils ont été instanciés en raison de sélecteurs correspondants.

Mettre à jour

  <component-to-inject #source></component-to-inject>
  <component-to-receive [injectedComponent]="source"></component-to-receive>

Avec

export class ReceivingComponent { 

  @Input() injectedComponent: InjectingComponent;

  ngOnInit() {
    console.log(this.injectedComponent);
  }      
}
2
Lee Winder 8 août 2016 à 19:40

Afin d'obtenir une référence à un composant présent dans votre modèle, vous pouvez utiliser l'annotation @ViewChild.

@ViewChild(TopLevelComponent)
private myTopLevelComponent:TopLevelComponent

Cela vous donnera la première instance de TopLevelComponent que angular trouve dans le modèle correspondant.

Si vous avez plusieurs composants du même type mais que vous voulez en trouver un certain, vous pouvez utiliser une variable de modèle locale.

@ViewChild('templateVariableName')
private myTopLevelComponent:TopLevelComponent

Votre modèle html devrait ressembler à ceci:

<top-level-component #templateVariableName></top-level-component>

Et enfin: si vous souhaitez trouver tous les composants du même type, vous pouvez utiliser l'annotation @ViewChildren.

@ViewChildren(TopLevelComponent)
private topLevelComponents:QueryList<TopLevelComponent>

Cela vous donnera une liste de tous les composants de premier niveau dans votre modèle.

Pourquoi deux instances sont-elles créées dans votre exemple?

L'utilisation du tableau des fournisseurs et la demande d'injection de cette classe dans votre constructeur indique à angular de créer une instance de votre classe de composants. La deuxième instance est créée, lorsque angulaire analyse votre modèle.

Notez que l'instance que vous obtenez injectée via le constructeur n'est pas la même que celle qui est présente dans votre modèle. Toutes les modifications que vous apportez à l'instance injectée ne seront pas reflétées à l'écran.

Pour obtenir l'instance correcte, vous devez suivre les étapes fournies ci-dessus.

1
Johannes Kling 8 août 2016 à 19:14

Angular2 transmet la liste des services chez les fournisseurs. Le service est un extrait de code réutilisable.

Exemple de service de base pouvant être utilisé dans n'importe quelle directive / composant.

imports ...

    @Injectable()
    export class MyEmpService {
        getAllEmployees() {
            // hit database
            // return employee list
        }
        getEmp(id: number) {
            // hit database
            // return employee data 
        }
}

Le composant est une directive avec vue. Angular2 transmet la liste des composants dans les directives. Tout composant de la liste peut être utilisé dans le composant. Encore une fois, c'est du code réutilisable mais avec vue. Exemple simple: EmployeeComponent.ts

imports...

@Component({
    selector: 'my-emp',
    template: `
        <button (click)="getEmpList()">Get All Emp</button>
        <button (click)="getEmp(2)">Get Emp</button>
        <div>
            {{result | json}}
        </div>
    `,
    providers: [MyEmpService]
})

export class EmployeeComponent{
    private result:any = '';
    constructor(private myEmpService: MyEmpService) { }

    getEmpList(){
        this.result = myEmpService.getAllEmployees();
    }
    getEmp(id: number){
        this.result = myEmpService.getEmp(id);
    }
}

AppComponent.ts

imports...

@Component({
    selector: 'my-app',
    template: `
        <h1>Main Component</h1>
        <my-emp></my-emp>
    `,
    directives: [EmployeeComponent]
})

export class AppComponent{ }
0
Arun Kumar 8 août 2016 à 18:45