25 votes

Angulaire 2: Injecter de service dans la classe

J'ai angulaire de la classe qui représente la forme. Je veux être en mesure d'instancier plusieurs instances de cette classe à l'aide de constructeur.

Le constructeur prend plusieurs arguments représentant les propriétés de cette forme.

constructor(public center: Point, public radius: number, fillColor: string,
    fillOpacity: number, strokeColor: string, strokeOpacity: number, zIndex: number)

À l'intérieur de ma classe je veux utiliser un service qui fournit une capacité à dessiner des formes sur la carte. Est-il possible d'injecter de service dans ma classe, et continuer à utiliser le constructeur de façon standard.

Donc, je veux faire quelque chose comme ci-dessous et Angulaire résoudre automatiquement injecté de la dépendance.

constructor(public center: GeoPoint, public radius: number, 
    fillColor: string, fillOpacity: number, strokeColor: string, strokeOpacity: number, 
    zIndex: number, @Inject(DrawingService) drawingService: DrawingService)

33voto

Liplattaa Points 456

J'ai réussi à résoudre mon problème.

Angulaire 2 - 4 fournit réfléchissant injecteur qui permet d'injecter des dépendances à l'extérieur des paramètres du constructeur.

Tout ce que j'avais à faire était de les importer Réfléchissant injecteur d' @angular/core.

import {ReflectiveInjector} from '@angular/core';

Et puis:

let injector = ReflectiveInjector.resolveAndCreate([DrawingService]);
this.drawingApi = injector.get(DrawingService);

La classe n'avez même pas à être décorés avec de la @Injectable décorateur. Le seul problème c'est que je dois fournir toutes les dépendances DrawingService et toutes imbriquées les dépendances, de sorte qu'il est difficile à maintenir.

EDIT:

Angulaire 5

import { Injector } from "@angular/core";

const injector = Injector.create([
    { provide: DrawingService }
]);
this.drawingApi = injector.get(DrawingService);

Angulaire 6

import { Injector } from "@angular/core";

const injector = Injector.create({ 
  providers: [ 
    { provide: DrawingService },
  ]
});
this.drawingApi = injector.get(DrawingService);

14voto

Ovidiu Dolha Points 3496

Voici deux autres façons possibles pour atteindre l'objectif souhaité, ou très semblable résultat.

Première approche à l'aide d'un gestionnaire pour votre entités ou non des objets de service

Vous avez un ou plusieurs service de l'usine(s) qui est/sont responsable de l'instanciation de vos objets.

Cela signifie qu' il peut fournir le deps encore à des objets, et ne nécessite pas que vous les passez sur vous-même.

Par exemple, disons que vous avez entités comme une hiérarchie de classe:

abstract class Entity { }

class SomeEntity extends Entity { 
   ...
}

Vous pouvez alors avoir un EntityManager qui est un service et peut construire des entités:

@Injectable()   // is a normal service, so DI is standard
class EntityManager {

  constructor(public http: Http) { }    // you can inject any services now

  create<E extends Entity>(entityType: { new(): E; }): E {
    const entity = new entityType();    // create a new object of that type
    entity.manager = this;              // set itself on the object so that that object can access the injected services like http - one can also just pass the services not the manager itself
    return entity;
  }

}

Vous pouvez aussi avoir des paramètres de construction si vous voulez (mais ils ne seront pas avoir n'importe quel type d'information, car create doit travailler avec tous les types d'entités):

class SomeEntity extends Entity { 
   constructor(param1, param1) { ... }
}

// in EntityManager
create<E extends Entity>(entityType: { new(): E; }, ...params): E {
    const entity = new entityType(...params);
    ...
}

Votre entités peuvent déclarer le gestionnaire:

abstract class Entity {
  manager: EntityManager;
}

Et votre entités pouvez l'utiliser pour faire n'importe quoi:

class SomeEntity extends Entity {
  doSomething() {
    this.manager.http.request('...');
  }
}

Maintenant, chaque fois que vous avez besoin de créer une entité ou un objet que vous utilisez ce gestionnaire. L' EntityManager doit être injecté lui-même, mais les entités sont des objets libres. Mais tous angulaire code de démarrer à partir d'un contrôleur ou d'un service ou d'une telle sorte qu'il sera possible d'injecter le gestionnaire.

// service, controller, pipe, or any other angular-world code

constructor(private entityManager: EntityManager) {
    this.entity = entityManager.create(SomeEntity);
}

Cette approche peut également être adapté à des objets arbitraires. Vous n'avez pas besoin d'une hiérarchie de classe, mais avec des caractères d'imprimerie cela fonctionne mieux. Il est également judicieux d'avoir une certaine classe de base pour les objets que vous pouvez ré-utiliser le code de cette vieille façon, surtout dans un domaine/l'approche orientée objet.

PROS: Cette approche est plus sûr parce qu'il se trouve encore sur la pleine DI hiérarchie et il devrait y avoir moins d'effets secondaires non désirés.

INCONVÉNIENTS: L'inconvénient est que vous ne pouvez jamais utiliser new jamais de nouveau, et vous ne pouvez obtenir l'accès à ces services en code arbitraire. Vous avez toujours besoin de s'appuyer sur la DI et de votre usine ou les usines.

La deuxième approche - h4ckz0rs

Vous créer un service dédié à l'obtention d' (via DI) les services dont vous avez besoin dans les objets.

Cette partie est très semblable à la première approche, c'est juste que ce service n'est pas une usine. Au lieu de cela, il passe sur l'injection de services dans un objet qui est défini en dehors de cette classe dans un fichier différent (explication après). Par exemple:

...
import { externalServices } from './external-services';

@Injectable()
export class ExternalServicesService {

  constructor(http: Http, router: Router, someService: SomeService, ...) {
    externalServices.http = http;
    externalServices.router = router;
    externalServices.someService = someService;
  }

}

L'objet qui va contenir le services est définie dans son propre fichier en tant que tel:

export const externalServices: {
  http,
  router,
  someService
} = { } as any;

Notez que les services ne sont pas à l'aide de n'importe quel type d'information (ce qui est un inconvénient, mais nécessaire).

Ensuite, vous devez assurez-vous que ExternalServicesService obtient injecté une fois. Le mieux est d'utiliser l'application principale composante:

export class AppComponent {

  constructor(..., externalServicesService: ExternalServicesService) {

Enfin, vous pouvez maintenant utiliser les services de n'importe quel objet à tout moment après l'application principale composante a été instancié.

import { externalServices } from '../common/externalServices' // or wherever is defined

export class SomeObject() {
    doSomething() {
        externalServices.http().request(...) // note this will be called after ng2 app is ready for sure
    }
}

Remarque vous ne serez pas capable de composer l'un de ces services dans le code de la classe ou dans les objets non instancié après l'application est instancié. Mais dans une application classique, cela ne devrait jamais être nécessaire.

Maintenant, quelques explications au sujet de cette étrange configuration:

Pourquoi utiliser un objet externalServices dans un fichier séparé au lieu de le même fichier, ou tout simplement réduire les services à la classe elle-même (comme les attributs statiques) et pourquoi les services non typée?

La raison en est que, lorsque vous êtes bulding le code, par exemple via angulaires-cli / webpack avec --prod mode, il est très probable d'obtenir cyclique des dépendances qui ne peut pas être résolu correctement et vous aurez laid erreurs qui sont difficiles à trouver - J'ai déjà vécu ça :).

Une erreur de la forme

Impossible de lire la propriété de "prototype" undefined

vu que lors de l'exécution avec --prod drapeau suggérer le fait que les dépendances ne sont pas réglés correctement.

C'est beaucoup mieux pour s'assurer que ExternalServicesService ne dépend externalServices pour passer les instances de service, et l'application n'injecte ExternalServicesService une fois (par exemple dans votre principale AppComponent) alors tous les code arbitraire / objets utilisera seulement l' externalServices d'obtenir les services.

Ainsi, un tel code ne devez importer l' externalServices qui n'a plus d'deps (parce que les services sont également pas tapé). Si ils étaient à l'importation ExternalServicesService il aurait importé tout le reste et n'aurait pas été en mesure de résoudre le bi-directionnelle deps de manière statique. Et cela devient un problème majeur dans ng2/webpack lorsque le regroupement pour la prod.

La même arriverait-il si nous étions à utiliser des types pour les services, parce que cela va exiger imports.

PROS: Cette approche est plus facile à utiliser une fois que l'installation a été faite, et vous êtes libre de les utiliser new. Fondamentalement n'importe quel fichier de code pouvez importer l' externalServices et ont un accès instantané à ces services, vous souhaitez exposer de cette façon.

INCONVÉNIENTS: l'inconvénient est Le hackish d'installation et les problèmes causés par cyclique de deps. Il est également plus sensible, vous ne pouvez pas être sûr d' externalServices a ces services tout de suite. Ils ne seront définis une fois pour le ng2 application démarre et l' ExternalServicesService est la première injection. Un inconvénient est que vous n'avez plus le type d'informations sur ces services.


PS: je ne sais pas pourquoi ce sujet n'est pas plus populaire.

Par exemple, étant un fan de domaine-conception orientée, ayant de puissantes entités (par exemple, avec des méthodes orientées au REPOS des appels ou d'interagir avec d'autres services) est important, et cette limitation fait toujours difficile.

Nous avons eu à surmonter cette limitation, à la fois dans angularjs, et à nouveau en Angular2+ comme il ne semble pas encore être abordées dans la bibliothèque.

2voto

Ayman Points 1774

Comme Angulaire 5.x:

import { Injector } from "@angular/core";
export class Model {

    static api: Api;

    constructor(data: any) {

        // check the api ref not exist
        // We don't want to initiate a new object every time
        if (!Model.api){
            //try inject my api service which use the HttpClient
            const injector: any = Injector.create([{ provide: Api, useClass: Api, deps: [] }]);
            Model.api = injector.get(Api);
        }

        // .....

    }
}

-2voto

thierry templier Points 998

En fait, vous ne pouvez pas. La classe doit être décorés avec de la @Injectable laisser Angular2 injecter des choses. L'@Inject décorateur est "seulement" il y spécifier des métadonnées supplémentaires au sujet de ce à injecter.

Dans votre cas, la classe est géré par vous, car la plupart de son constructeur paramètres ne correspondent pas à des dépendances et sont fournis lorsque vous instancier explicitement la classe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X