Angular-通用服务提供商

时间:2019-06-18 16:15:36

标签: angular typescript

我已经为我的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>) { }

1 个答案:

答案 0 :(得分:0)

因为您想为不同的端点提供相同的服务,所以将端点配置提供为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作为参数的函数,该函数创建具有唯一endPoints的实例。

{
    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

ps。我是从this post得到这个想法的。我改进了打字方式并合并了更改,以使服务具有通用性