首页 > 解决方案 > Angular - 通用服务的提供者

问题描述

我为我的 HTTP API 创建了一个通用服务,我想为不同的端点提供这个服务,而不为我的每个端点创建一个类。我的服务看起来像这样

export class ApiService<T> {
  constructor(private httpClient: HttpClient, private otherDependency: OtherDependency, private subPath: string, private defaultHeaders = []) { }
}

我正在使用这些令牌和提供程序注入它

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

export const XXXToken = new InjectionToken<ApiService<XXX>('MyService');
export const XXXProvider = {
  provide: XXXToken,
  useFactory: (httpClient: HttpClient, dep: OtherDependency) => new ApiService<XXX>(httpClient, dep, 'xxx'),
  deps: [ HttpClient, OtherDependency]
};

尝试在 NgModule 的声明中使用我的提供程序并在生产模式下构建(仅在生产模式下)时,以前的代码实际上会失败。错误看起来像

 Error during template compile of 'AppModule'
 Consider changing the function expression into an exported function.

问题来自(httpClient: HttpClient, dep: OtherDependency) => new ApiService<XXX>(httpClient, dep, 'xxx')

如果我做类似的事情,我可以让它工作

export function myFactory(httpClient: HttpClient, dep: OtherDependency) => new ApiService<XXX>(httpClient, dep, 'xxx');
export const XXXProvider = {
  provide: XXXToken,
  useFactory: myFactory,
  deps: [ HttpClient, OtherDependency]
};

但是,因为提供者和工厂比这更复杂,所以我创建了一个辅助函数,以便在添加新端点时不必重复该代码。

export func getProvider(token, subPath) {
  return {
    provide: token,
  useFactory: (httpClient: HttpClient, dep: OtherDependency) => new ApiService<XXX>(httpClient, dep, subPath),
  deps: [ HttpClient, OtherDependency]
  }
}

在那里我不能使用导出功能技巧,因为我希望每次创建新服务时我的子路径都不同。

有没有办法解决这个问题,或者我是否坚持在每次需要时重复我的提供程序创建。我会非常难过,因为这意味着例如,每当我需要对该服务的新依赖时,我都必须更改每个提供程序的创建。=== 编辑 === 我希望能够轻松地将我的服务注入到任何服务/组件中,而不必将那些其他对象绑定到服务创建或实现细节中,并且一个组件中可以有许多服务。理想情况下,我想要这样的东西

export class MyComponent implements OnInit {
  constructor(private userService: ApiService<User>, private countryService: ApiService<countryService>) { }

  ngOnInit() {
    userService.get();
    countryService.get();
  }
}

但是因为 javascript 并不真正支持泛型,所以这行不通,因为 ApiService 和 ApiService 实际上是同一个类。因此,无论如何我目前都需要注入令牌,所以我可以接受

export class MyComponent implements OnInit {
  constructor(@Inject(UserToken) private userService: ApiService<User>, @Inject(CountryToken) private countryService: ApiService<countryService>) { }

标签: angulartypescript

解决方案


因为您想为不同的端点提供相同的服务,所以提供端点配置InjectionToken并将该令牌注入您的服务将使您的生活更轻松。所以改变你的服务定义如下;

export const MY_API_ENDPOINT = new InjectionToken<string>('MY_API_ENDPOINT');

@Injectable()
export class ApiService<T> {
  constructor(@Inject(MY_API_ENDPOINT) private endPoint: string) { }
}

当您注入ApiService应用程序时,请MY_API_ENDPOINT在提供程序级别进行配置(无论是在组件级别还是在模块级别)

在组件级别

@Component({
  selector: 'app-component1',
  templateUrl: './component1.component.html',
  styleUrls: ['./component1.component.css'],
  providers: [ApiService, { provide: MY_API_ENDPOINT, useValue: "api.end.point.1" }]
})
export class Component1Component implements OnInit {}

或在模块级别

@NgModule({
  imports: [CommonModule],
  declarations: [Component3Component],
  exports: [Component3Component],
  providers: [ApiService, { provide: MY_API_ENDPOINT, useValue: "api.end.point.3" }]
})
export class App2Module { }

我创建了一个演示,演示了具有不同端点的组件和模块级别提供程序

https://stackblitz.com/edit/angular-4b5yb7

===== 第二个解决方案 =====

如果组件需要具有两个不同端点的相同服务,则上述解决方案将不起作用。所以我们需要一种方法来创建具有不同参数的相同服务实例。遵循您创建创建服务实例的工厂的原始方法;我修改了工厂函数,它返回一个以 endPoint 作为参数的函数,并且该函数创建具有唯一端点的实例。

{
    provide: ApiService, useFactory:
      (someOtherService: SomeOtherService) => {
        return (endPoint: string) => {
          return new ApiService(endPoint, someOtherService);
        }
      }, deps: [SomeOtherService /** this is just an example to show how to inject other dependencies*/]
}

并在组件中按如下方式使用

export class Component1Component {
  apiService1 = this.apiFactory<string>("end.point.1");
  apiService2 = this.apiFactory<number>("end.point.2");

  constructor(@Inject(ApiService) private apiFactory: <T>(endPoint: string) => ApiService<T>) {}
}

这是一个工作演示https://stackblitz.com/edit/angular-6kmy8w

附言。我从这篇文章中得到了这个想法。我改进了打字并合并了使服务通用的更改


推荐阅读