简体   繁体   English

Angular - 通用服务的提供商

[英]Angular - Providers for generic services

I have created a generic service for my HTTP APIs and I want to provide this service for different endpoint without creating a class for each of my endpoints. 我为我的HTTP API创建了一个通用服务,我想为不同的端点提供这项服务,而不为每个端点创建一个类。 My service looks like this 我的服务看起来像这样

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

And I am injecting it using tokens and providers like these ones 我正在使用令牌和提供商这样注入它

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

Previous code will actually fail when trying to use my provider in the declarations of a NgModule and building in production mode (only in production mode). 尝试在NgModule的声明中使用我的提供程序并在生产模式下构建时(仅在生产模式下),以前的代码实际上会失败。 Error looks like 错误看起来像

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

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

I can make it work if I do something like 如果我这样做,我可以使它工作

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

However, because the provider and factory is more complicated than that, I've created an helper function to not have to repeat that code any time I am adding a new endpoint. 但是,因为提供者和工厂比这更复杂,所以我创建了一个帮助函数,以便在我添加新端点时不必重复该代码。

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

And in there I cannot use the export function trick because I want my subpath to be different everytime I am creating a new service. 在那里我不能使用导出功能技巧,因为我希望每次创建新服务时我的子路径都不同。

Is there a way to get around this or am I stuck to repeating my provider creation every time I need it. 有没有办法解决这个问题,或者我每次需要时都会重复创建提供程序。 I would be very sad since that would mean for example that I would have to change every provider creation anytime I need a new dependency on that service. 我会非常难过,因为这意味着我需要在我需要对该服务的新依赖时随时更改每个提供者创建。 === Edit === I want to be able to inject my services easily in any service/component without having to bin those other objects to the service creation or implementation detailes and there can be many services in one component. ===编辑===我希望能够在任何服务/组件中轻松注入我的服务,而不必将这些其他对象绑定到服务创建或实现细节,并且在一个组件中可以有许多服务。 Ideally I would like something like that 理想情况下,我想要这样的东西

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

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

But because javascript does not realy support generics, this cannot work because ApiService and ApiService are actually the same class. 但是因为javascript并不真正支持泛型,所以这不起作用,因为ApiService和ApiService实际上是同一个类。 Therefore I currently need injection token anyway so I would be OK with 因此我目前还需要注射令牌,所以我会好的

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

since you want to provide same service with different end points, providing your end point config as an InjectionToken and injecting that token in your service would make your life easier. 因为您希望提供具有不同端点的相同服务,将您的端点配置作为InjectionToken并在您的服务中注入该令牌将使您的生活更轻松。 so change your service definition as follows; 所以改变你的服务定义如下;

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

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

when you inject ApiService through out your application, configure MY_API_ENDPOINT at provider level (doesn't matter whether it is at Component or Module level) 当您在ApiService应用程序中注入ApiService时, MY_API_ENDPOINT在提供程序级别配置MY_API_ENDPOINT (无论是在组件级还是模块级别)

in component level 在组件级别

@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 {}

or in module level 或者在模块级别

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

i created a demo that demonstrates both Component and Module level providers with different endpoints 我创建了一个演示,演示了具有不同端点的组件级和模块级提供程序

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

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

above solution will not work if a component needs same service with two different end points. 如果组件需要具有两个不同端点的相同服务,则上述解决方案将不起作用。 so we need a way to create same service instances with different parameters. 所以我们需要一种方法来创建具有不同参数的相同服务实例。 following you original approach of creating a factory that creates service instances; 按照您创建创建服务实例的工厂的原始方法; i modified the factory function that returns a function which takes endPoint as parameter, and that function creates instances with unique endPoints. 我修改了工厂函数,该函数返回一个以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*/]
}

and use it as follows in component 并在组件中使用如下

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

here is a working demo https://stackblitz.com/edit/angular-6kmy8w 这是一个工作演示https://stackblitz.com/edit/angular-6kmy8w

ps. PS。 i got the idea from this post . 我从这篇文章中得到了这个想法。 i improved typing and incorporated changes for making the service generic 我改进了打字并整合了使服务变得通用的变更

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Angular2 - 重用具有不同服务/提供者的组件 - Angular2 - reuse component with different services/providers Angular:当需要大量服务时简化“提供者” - Angular: Simplify the "providers" when lots of services are required 在 Angular 中注入通用服务 - Injection of Generic Services in Angular 为什么 Angular CLI 不自动在提供者数组中添加服务? - Why doesn't Angular CLI add services in providers array automatically? 创建简单的自定义元素(@angular/elements)时出现 NullInjectorError。 没有任何提供者、服务 - NullInjectorError while creating simple custom element (@angular/elements). without any providers, services 创建从 3rd 方库提供服务的 Angular 共享库和模块是一种好习惯吗? - Is it a good practice to create an Angular shared library and module that providers services from 3rd party libraries? 在 Angular Jasmine 测试中使用具有不同通用类型的相同 class 的两个提供程序 - Use two providers for the same class with different generic typing in Angular Jasmine Tests 在多个组件的提供者中声明服务 - Declaring services in providers of multiple components Angular Dart提供商 - Angular Dart providers Angular 2使用现有提供者 - Angular 2 useExisting providers
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM