角度 |将服务注入装饰器

问题描述 投票:0回答:6

我正在尝试将服务注入到我正在创建的装饰器函数中,这样我就可以获得存储在变量内的内容。

我目前有一个基本的装饰器设置。

export function canEdit(editName: string, service: Service) {
      return function (constructor: any) {
          console.log(constructor);
      }
}

我使用它的地方是要求我提供第二个参数,这显然需要依赖注入。

我还尝试了函数中不允许的

@Inject(Service)

有什么办法可以做到这一点吗?

或者是否可以在装饰器中获取父类变量?

angular typescript decorator
6个回答
25
投票

装饰器是一种 TypeScript 功能,在 Angular 的依赖注入系统之外工作。

我知道的唯一解决方案是创建一个特殊的服务,将依赖关系公开给装饰器。

@Injectable()
export class DecoratorService {
     private static service: Service | undefined = undefined;
     public constructor(service: Service) {
         DecoratorService.service = service;
     }
     public static getService(): Service {
         if(!DecoratorService.service) {
             throw new Error('DecoratorService not initialized');
         }
         return DecoratorService.service;
     }
}

您现在可以通过上面的类访问注入的服务。

export function canEdit(editName: string) {
      return function (constructor: any) {
          const service = DecoratorService.getService();
          // ^^^ access dependency here
          console.log(constructor);
      }
}

除非 Angular 应用程序中的某些内容依赖于

DecoratorService
,否则上述内容将不起作用,因为 Angular 会在需要时创建实例。所以你可以将它注入到模块中以强制其初始化。

@NgModule({
    provides: [
        DecoratorService
    ]
})
export class MainModule {
    public constructor(service: DecoratorService) {
                      // ^^^ forces an instance to be created
    }
}

20
投票

我知道我参加聚会迟到了,但是,还有另一种方法可以访问装饰器中的服务。

在需要必要服务的模块中暴露 Angular 的 Injector:

@NgModule({
  declarations: [],
  imports: [],
  providers: [SomeService]
})
export class SharedModule {
  static injector: Injector;

  constructor(injector: Injector) {
    SharedModule.injector = injector;
  }
}

通过模块注入器属性从装饰器内部获取服务:

export const MyDecorator = (): any => {

  return (target: object, key: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor => {
    const originalMethod = descriptor.value;

    descriptor.value = function(...args: any[]): any {
      // access the service
      const service = SharedModule.injector.get<SomeService>(SomeService);

      // (...)
    };

    return descriptor;
  };
};

2
投票

@Ricky 的答案的改进/更新。

从 Angular 14+ 开始工作,无需将该服务包含在提供商列表中。我还遇到了很多延迟加载模块的问题,所以我直接在我的 AppModule 中添加了注入器,现在可以从任何地方使用。

应用程序.module.ts

export class AppModule {
  static injector: Injector;

  constructor(injector: Injector) {
    AppModule.injector = injector;
  }
}

我的装饰器.ts

import { AppModule } from './app.module';
import { MyService } from './my.service';

export const MyDecorator = (params: unknown): any => {
  return (component: Function) => {
    const service = AppModule.injector.get<MyService>(
      MyService
    );

    console.log(component, params, service);
  };
}

1
投票

非常同意@reactgular 的回答。

此外,从技术角度来看,它可以在没有任何服务的情况下完成,并且有一些限制。

我们必须使用 getPlatform 有自己的注入器并且可以与 Injectable({provided: 'platform'}) 一起使用来完成此任务。

import { getPlatform, inject, Injectable } from '@angular/core';  

@Injectable({ providedIn: 'platform' })
export class SomeService { 
}

export function someDecorator<T extends { new (...args: any[]): {} }>(
  constructor: T
) {
  const someService = getPlatform()?.injector.get(SomeService);

  console.log(someService);

  return constructor;
}


0
投票

Angular 依赖注入仅在依赖于另一个服务的组件类或 Angular 服务中起作用。

依赖项(即角度组件)的使用者,在其构造函数中声明它需要什么,并让框架提供它们。

有关依赖注入的更多信息,请参阅官方 Angular 文档:

依赖注入的实际应用

https://next.angular.io/guide/dependency-injection-in-action


0
投票

这是我最简单的解决方案

interface HasInjector {
    injector: Injector;
}

function decoratorNeedsInjector() {
    return function (target, property, descriptor) {
        descriptor.value = function () {
            const injector = this.injector; // ! We access to `injector` property of the service
        };
    };
}

@Injectable()
class MyApiService implements HasInjector {
    @decoratorThatNeedsInjector()
    makeHttpRequest() {}
}

简单的概念:装饰器

decoratorNeedsInjector
说:

你想利用我吗?然后确保您实现了

HasInjector
接口。

您可以将

HasInjector
decoratorNeedsInjector
重命名为彼此相关的名称。

© www.soinside.com 2019 - 2024. All rights reserved.