Instanz des Dienstes ohne Konstruktorinjektion abrufen

80

Ich habe einen @InjectableDienst in Bootstrap definiert. Ich möchte die Instanz des Dienstes ohne Verwendung der Konstruktorinjektion erhalten. Ich habe versucht, ReflectiveInjector.resolveAndCreateaber das scheint eine neue Instanz zu erstellen.

Der Grund, den ich versuche, ist, dass ich eine Basiskomponente habe, die von vielen Komponenten abgeleitet ist. Jetzt muss ich auf einen Dienst zugreifen, aber ich möchte ihn nicht zum ctor hinzufügen, da ich den Dienst nicht auf alle abgeleiteten Komponenten injizieren möchte.

TLDR: Ich brauche eine ServiceLocator.GetInstance<T>()

UPDATE: Aktualisierter Code für RC5 +: Speichern der Injektorinstanz zur Verwendung in Komponenten

dstr
quelle

Antworten:

70

Ja, ReflectiveInjector.resolveAndCreate()erstellt eine neue und nicht verbundene Injektorinstanz.

Sie können die Angulars- InjectorInstanz injizieren und die gewünschte Instanz daraus abrufen

constructor(private injector:Injector) {
  injector.get(MyService);
}

Sie können die auch Injectorin einer globalen Variablen speichern und dann diese Injektorinstanz verwenden, um bereitgestellte Instanzen zu erfassen, wie beispielsweise in https://github.com/angular/angular/issues/4112#issuecomment-153811572 erläutert

Günter Zöchbauer
quelle
1
Das Speichern des Injektors in einer globalen Variablen in bootstrap.then () klingt wie das, was ich brauche.
dstr
59
Die Frage lautet ohne Konstruktor, was diese Antwort eher wenig hilfreich macht.
Jonathan Miles
1
Wenn sich der Konstruktor in einer Komponente befindet, ist dies der Komponenteninjektor, in einem Dienst der Modulinjektor (im Grunde der Rootinjektor, wenn es sich nicht um ein faul geladenes Modul handelt). Sie können den Injektor für übergeordnete Komponenten auch durch Hinzufügen erhalten @SkipSelf(). Sie können auch den parentGetter interagieren , um den Wurzelinjektor zu erhalten.
Günter Zöchbauer
2
@ Rhyous Magie? Sie können den Injektor in einem statischen Feld oder einer globalen Variablen freigeben und von dort aus darauf zugreifen. Zuerst müssen Sie ihn jedoch mithilfe eines Konstruktors "irgendwo" injizieren und dem statischen Feld zuweisen.
Günter Zöchbauer
1
Wird der DI-Container ng so schlecht verwendet, dass er nicht einmal die Grundfunktionen eines gemeinsamen DI-Containers unterstützt?
Rhyous
58

In dem aktualisierten Winkel, in dem ngModules verwendet werden, können Sie eine Variable erstellen, die an einer beliebigen Stelle im Code verfügbar ist:

export let AppInjector: Injector;

export class AppModule {
  constructor(private injector: Injector) {
    AppInjector = this.injector;
  }
}

Jetzt können Sie mit den Funktionen AppInjectorjeden Dienst in einem beliebigen Code finden.

import {AppInjector} from '../app.module';

const myService = AppInjector.get(MyService);
Elias Rezer
quelle
1
Nett. Könnte auch eine gute Antwort unter stackoverflow.com/questions/39409328/… sein .
Arjan
1
Du bist der König!
Davide Perozzi
Wow dürfen wir das machen?
Simon_Weaver
2
Dies hat ein kleines Problem. Sie müssen separate Injektoren für träge geladene Module herstellen, die einen Service bieten.
Vahid
7

Ein anderer Ansatz würde darin bestehen, einen benutzerdefinierten Dekorator zu definieren (a CustomInjectable, um die Metadaten für die Abhängigkeitsinjektion festzulegen:

export function CustomComponent(annotation: any) {
  return function (target: Function) {

    // DI configuration
    var parentTarget = Object.getPrototypeOf(target.prototype).constructor;
    var parentAnnotations = Reflect.getMetadata('design:paramtypes', parentTarget);

    Reflect.defineMetadata('design:paramtypes', parentAnnotations, target);

    // Component annotations / metadata
    var annotations = Reflect.getOwnMetadata('annotations', target);
    annotations = annotations || [];
    annotations.push(annotation);
    Reflect.defineMetadata('annotations', annotations, target);
  }
}

Es nutzt die Metadaten des übergeordneten Konstruktors anstelle seiner eigenen. Sie können es für die untergeordnete Klasse verwenden:

@Injectable()
export class SomeService {
  constructor(protected http:Http) {
  }
}

@Component()
export class BaseComponent {
  constructor(private service:SomeService) {
  }
}

@CustomComponent({
  (...)
})
export class TestComponent extends BaseComponent {
  constructor() {
    super(arguments);
  }

  test() {
    console.log('http = '+this.http);
  }
}

Siehe diese Frage für weitere Details:

Thierry Templier
quelle
2
Das Problem ist, dass ich mit tsc nicht super (Argumente) aufrufen kann, da die Argumente für den Basiskonstruktor nicht übereinstimmen.
Parlament
1
In diesem speziellen Fall können wir den Konstruktor einfach aus der abgeleiteten Klasse github.com/Microsoft/TypeScript/issues/12439
slowkot
-1

StoreService .ts

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

    @Injectable()
    export class StoreService {
      static isCreating: boolean = false;
      static instance: StoreService ;

      static getInstance() {
        if (StoreService.instance == null) {
          StoreService.isCreating = true;
          StoreService.instance = new StoreService ();
          StoreService.isCreating = false;
        }
        return StoreService.instance;
      }
      constructor() {
        if (!StoreService.isCreating) {
          throw new Error('You can\'t call new in Singleton instances! Call StoreService.getInstance() instead.');
        }
     }

  MyAlertMethod(){
    alert('hi);
    }
  }

component.ts

//call this service directly in component.ts as below:-

 StoreService.getInstance().MyAlertMethod();
Mahi
quelle
Wie kann dies mit Diensten funktionieren, für die andere im Konstruktor eingefügte Dienste erforderlich sind?
Emeke Ajeh