如何将服务注入类(不是组件)


88

我想将服务注入到不是组件的类中。

例如:

我的服务

import {Injectable} from '@angular/core';
@Injectable()
export class myService {
  dosomething() {
    // implementation
  }
}

我的课

import { myService } from './myService'
export class MyClass {
  constructor(private myservice:myService) {

  }
  test() {
     this.myservice.dosomething();
  }
}

该解决方案不起作用(我认为是因为MyClass尚未实例化)。

有没有其他方法可以在类(而非组件)中使用服务?还是您会认为我的代码设计不合适(在不是组件的类中使用服务)?

谢谢。

Answers:


56

注入仅适用于由Angulars依赖注入(DI)实例化的类。

  1. 你需要
    • 添加@Injectable()MyClass
    • 提供MyClassproviders: [MyClass]在组件或NgModule中。

当您随后在MyClass某个位置进行注入时,MyService实例将被传递给MyClassDI实例化的实例(在首次注入之前)。

  1. 另一种方法是配置自定义注入器,例如
constructor(private injector:Injector) { 
  let resolvedProviders = ReflectiveInjector.resolve([MyClass]);
  let childInjector = ReflectiveInjector.fromResolvedProviders(resolvedProviders, this.injector);

  let myClass : MyClass = childInjector.get(MyClass);
}

这种方式myClass将是一个MyClass实例,由Angulars DI实例化,并将在实例化时myService注入MyClass
另请参阅在指令中手动从Injector获取依赖项

  1. 还有另一种方法是自己创建实例:
constructor(ms:myService)
let myClass = new MyClass(ms);

2
我认为将服务注入独立类是一种反模式。
tomexx '17

11
当然,但有时仍有意义,它总是好的,知道什么是可能的
君特Zöchbauer

为什么要在构造函数中创建一个新的注射器?为什么不使用注射的那一种呢?如果您要创建一个新的,则没有理由先注入一个
smac89 '17

新的是带有其他提供程序的儿童注射器。如果添加到子注入器的提供程序依赖于父组件或模块级别提供的提供程序,则DI可以自动解决所有问题。因此,在某些情况下肯定是有道理的。
君特Zöchbauer

1
@TimothyPenz感谢您的编辑。在此示例中,这private不是必需的,因为injector未在构造函数之外使用,因此无需在字段中保留引用。
君特Zöchbauer

29

这不是问题的直接答案,但是如果您出于我的原因而正在阅读此文章,则可能会有所帮助...

假设您正在使用ng2-translate,而您确实希望您的User.ts班级拥有它。您立即想到要使用DI来放入它,毕竟您是在做Angular。但这有点过分考虑,您可以将其传递到构造函数中,或者将其设置为从组件设置的公共变量(大概是在其中进行DI插入)。

例如:

import { TranslateService } from "ng2-translate";

export class User {
  public translateService: TranslateService; // will set from components.

  // a bunch of awesome User methods
}

然后从注入TranslateService的一些用户相关组件

addEmptyUser() {
  let emptyUser = new User("", "");
  emptyUser.translateService = this.translateService;
  this.users.push(emptyUser);
}

希望这可以帮助像我这样的人,因为我们有时太聪明了,这些人将要编写很多难以维护的代码=]

(注意:您可能想设置一个变量而不是使其成为构造函数方法的一部分的原因是,您可能会遇到不需要使用该服务的情况,因此始终需要传递它意味着引入了额外的导入/从未真正使用过的代码)


如果忘记设置它,则可能进行translateServiceget / setget抛出有意义的错误而不是NullReference异常
Simon_Weaver

1
在类构造函数中使translateService成为必需的参数也许更有意义?此模式更符合DI模式的恕我直言。
Icycool

15

这有点()hacky,但是我想我也应该分享我的解决方案。请注意,这仅适用于Singleton服务(注入到应用程序根目录,而不是组件!),因为它们与您的应用程序一样有效,并且只有一个实例。

首先,为您服务:

@Injectable()
export class MyService {
    static instance: MyService;
    constructor() {
        MyService.instance = this;
    }

    doSomething() {
        console.log("something!");
    }
}

然后在任何班级:

export class MyClass {
    constructor() {
        MyService.instance.doSomething();
    }
}

如果您想减少代码混乱并且始终不使用非单一服务,则此解决方案很好。


但是,如何在MyClass中使用MyService而不在构造函数中进行任何声明?
Akhil V

@AkhilV只需导入MyService即可,就像导入其他任何类一样,然后可以按照说明直接调用该方法。
希尔夫斯

13

locator.service.ts

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

export class ServiceLocator {
    static injector: Injector;
}

app.module.ts

@NgModule({ ... })

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

poney.model.ts

export class Poney {

    id: number;
    name: string;
    color: 'black' | 'white' | 'brown';

    service: PoneyService = ServiceLocator.injector.get(PoneyService); // <--- HERE !!!

    // PoneyService is @injectable and registered in app.module.ts
}

1
不确定OP方案的最佳实践是什么,但是这个答案似乎比领先的答案简单得多。将injector.get()调用移至模块是否有效(假设无状态服务,以便多个类可以“共享”同一实例)?
G0BLiN

我认为代码将以所有共享同一个Poney服务实例的poney实例结束。你怎么看?
朱利安

朱利安(Julien)-对于我的情况而言,这是可以接受的结果(实际上是首选结果)-想像输入验证器,用户权限处理程序或本地化服务之类的东西-您在整个应用程序中都需要相同的功能,但每个应用程序都不需要唯一的实例服务的个人消费者。
G0BLiN

3

如果您的服务方法是纯函数,则解决此问题的一种干净方法是在服务中使用静态成员。

您的服务

import {Injectable} from '@angular/core';
@Injectable()
export class myService{
  public static dosomething(){
    //implementation => doesn't use `this`
  }
}

你的班

export class MyClass{
  test(){
     MyService.dosomething(); //no need to inject in constructor
  }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.