생성자 주입없이 서비스 인스턴스 가져 오기


81

@Injectable부트 스트랩에 정의 된 서비스 가 있습니다 . 생성자 주입을 사용하지 않고 서비스 인스턴스를 가져오고 싶습니다. 나는 사용해 ReflectiveInjector.resolveAndCreate보았지만 새 인스턴스를 만드는 것 같습니다.

내가하려는 이유는 많은 구성 요소에서 파생 된 기본 구성 요소가 있기 때문입니다. 이제 서비스에 액세스해야하지만 모든 파생 구성 요소에 서비스를 주입하고 싶지 않기 때문에 ctor에 추가하고 싶지 않습니다.

TLDR : 나는 ServiceLocator.GetInstance<T>()

업데이트 : RC5 +에 대한 업데이트 된 코드 : 구성 요소에 사용할 인젝터 인스턴스 저장

답변:


72

예, ReflectiveInjector.resolveAndCreate()연결되지 않은 새 인젝터 인스턴스를 생성합니다.

Angulars Injector인스턴스 를 삽입 하고 다음을 사용하여 원하는 인스턴스를 가져올 수 있습니다.

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

또한 Injector일부 전역 변수에 를 저장 하고이 인젝터 인스턴스를 사용하여 예를 들어 https://github.com/angular/angular/issues/4112#issuecomment-153811572에 설명 된 것처럼 제공된 인스턴스를 획득 할 수 있습니다.


1
bootstrap.then ()의 전역 변수에 인젝터를 저장하는 것은 내가 필요한 것처럼 들립니다.
dstr

65
질문은이 답변을 오히려 도움이되지 않게 만드는 생성자가 없다고 말합니다.
Jonathan Miles

1
생성자가 구성 요소에 있으면 구성 요소 주입기이고 서비스에서는 모듈 주입기입니다 (지연로드 모듈이 아닌 경우 기본적으로 루트 주입기). 를 추가하여 상위 구성 요소 인젝터를 가져올 수도 있습니다 @SkipSelf(). parent루트 인젝터를 얻기 위해 getter를 삽입 할 수도 있습니다 .
Günter Zöchbauer

2
@Rhyous 마술? 정적 필드 또는 전역 변수에서 인젝터를 공유하고 거기에서 액세스 할 수 있지만 먼저 생성자를 사용하여 "어딘가에"주입하고 정적 필드에 할당해야합니다.
Günter Zöchbauer

1
DI 컨테이너가 일반적인 DI 컨테이너의 기본 기능을 지원하지 않는 것이 그렇게 나쁜가요?
Rhyous

61

ngModules가 사용되는 업데이트 된 Angular에서 코드의 모든 위치에서 사용 가능한 변수를 만들 수 있습니다.

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
당신이 왕입니다!
Davide Perozzi

와우 우리가 할 수 있습니까?
Simon_Weaver

2
이것은 작은 문제가 있습니다. 서비스를 제공하는 지연로드 모듈에 대해 별도의 인젝터를 만들어야합니다.
Vahid

7

또 다른 접근 방식은 사용자 지정 데코레이터 ( 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 (arguments)를 호출 할 수 없다는 것입니다.
의회

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

component.ts

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

 StoreService.getInstance().MyAlertMethod();

생성자에 삽입 된 다른 서비스를 필요로하는 서비스와 어떻게 작동 할 수 있습니까?
Emeke Ajeh 2010
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.