Отримання екземпляра служби без введення конструктора


81

У мене є @Injectableслужба, визначена в bootstrap. Я хочу отримати екземпляр служби, не використовуючи введення конструктора. Я спробував використовувати, ReflectiveInjector.resolveAndCreateале це, здається, створює новий екземпляр.

Причиною, яку я намагаюся зробити, є базовий компонент, похідний багатьма компонентами. Тепер мені потрібно отримати доступ до служби, але я не хочу додавати її до ctor, оскільки я не хочу вводити службу на всі похідні компоненти.

TLDR: мені потрібно ServiceLocator.GetInstance<T>()

ОНОВЛЕННЯ: Оновлений код для RC5 +: Зберігання екземпляра інжектора для використання в компонентах

Відповіді:


72

Так, ReflectiveInjector.resolveAndCreate()створює новий і не зв’язаний екземпляр інжектора.

Ви можете ввести Injectorекземпляр Angulars і отримати з нього потрібний екземпляр за допомогою

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

Ви також можете зберегти Injectorв якійсь глобальній змінній і, за допомогою цього екземпляра інжектора, придбати надані екземпляри, наприклад, як описано в https://github.com/angular/angular/issues/4112#issuecomment-153811572


1
Зберігання інжектора в глобальній змінній у bootstrap.then () звучить як те, що мені потрібно.
dstr

65
Питання говорить без конструктора, що робить цю відповідь досить непотрібною.
Джонатан Майлз

1
Якщо конструктор знаходиться в компоненті, це інжектор компонентів, у службі це модуль інжектора (в основному кореневий інжектор, якщо це не ледачий завантажений модуль). Ви також можете отримати інжектор батьківських компонентів, додавши @SkipSelf(). Ви також можете встановити parentгеттер для отримання кореневої інжектора.
Günter Zöchbauer

2
@Rhyous magic? Ви можете поділитися інжектором у статичному полі або глобальній змінній і отримати до нього доступ звідти, але спочатку потрібно ввести його "десь" за допомогою конструктора і призначити його статичному полю.
Günter Zöchbauer

1
Чи використовує DI-контейнер настільки погано, що навіть не підтримує основні функції загального DI-контейнера?
Rhyous

61

В оновленому Angular, де використовуються ngModules, ви можете створити змінну, доступну в будь-якому місці коду:

export let AppInjector: Injector;

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

Тепер ви можете використовувати, AppInjectorщоб знайти будь-яку послугу в будь-якому місці вашого коду.

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

const myService = AppInjector.get(MyService);

1
Приємно. Це може бути гарною відповіддю на stackoverflow.com/questions/39409328/… .
Arjan

1
Ти король!
Давіде Перозці,

Ого, нам це дозволено?
Simon_Weaver

2
Це має невелику проблему. Ви повинні зробити окремі форсунки для ліниво завантажених модулів, які надають послугу.
Вахід

7

Інший підхід полягав би у визначенні власного декоратора (a CustomInjectableдля встановлення метаданих для введення залежностей:

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

Він використовуватиме метадані батьківського конструктора замість власних. Ви можете використовувати його на дочірньому класі:

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

Докладніше див. У цьому питанні:


2
Проблема в тому, що tsc не дозволяє мені викликати super (аргументи), оскільки аргументи для базового конструктора не збігаються.
парламент

1
в цьому конкретному випадку ми можемо просто видалити конструктор з похідного класу 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);
    }
  }

компонент.ts

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

 StoreService.getInstance().MyAlertMethod();

Як це може працювати зі службами, для яких потрібні інші послуги, введені в конструктор?
Emeke Ajeh
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.