首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用forRoot向模块提供具有依赖关系的角度传递服务

使用forRoot向模块提供具有依赖关系的角度传递服务
EN

Stack Overflow用户
提问于 2020-06-16 16:12:46
回答 1查看 3K关注 0票数 4

我有一个基于JWT的身份验证服务。为了在我的所有项目中重用这个服务,我创建了一个应该随npm一起提供的库。

要使这个服务正常工作,我需要一些API调用。在每个项目中,API看起来可能完全不同,所以我不想在我的库中提供这个功能,而是注入另一个处理API调用的服务。

我的想法是创建一个包含我的服务的模块,并提供一个接口来描述API调用的服务并将其注入forRoot。问题是我的api服务有一些依赖项,比如HttpClient,我不能简单地在app.module中实例化它。

我的图书馆看起来:

auth.module.ts

代码语言:javascript
运行
复制
import { NgModule, ModuleWithProviders, InjectionToken } from '@angular/core';
import { AuthService } from '../services/auth.service';
import { AuthAPI } from '../models/authAPI';
import { AuthapiConfigService } from '../services/authapi-config.service';


@NgModule()
export class AuthModule {

  static forRoot(apiService: AuthAPI): ModuleWithProviders {
    return {
      ngModule: AuthModule,
      providers: [
        AuthService,
        {
          provide: AuthapiConfigService,
          useValue: apiService
        }
      ]
    };
  }
}

auth-api.interface.ts

代码语言:javascript
运行
复制
import { Observable } from 'rxjs';

export interface AuthAPI {
  reqLogin(): Observable<{ access_token: string; }>;
  reqRegister(): Observable<{ access_token: string; }>;
}

auth-api-config.service.ts

代码语言:javascript
运行
复制
import { InjectionToken } from '@angular/core';
import { AuthAPI } from '../models/authAPI';
/**
 * This is not a real service, but it looks like it from the outside.
 * It's just an InjectionTToken used to import the config object, provided from the outside
 */
export const AuthapiConfigService = new InjectionToken<AuthAPI>('API-Service');

auth.service.ts

代码语言:javascript
运行
复制
 constructor(@Inject(AuthapiConfigService) private apiService) {}

我是如何努力实施它的:

auth-rest-service.ts

代码语言:javascript
运行
复制
import { Injectable } from '@angular/core';
import { AuthAPI } from 'projects/library-project/src/lib/auth/models/authAPI';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class AuthRestService implements AuthAPI  {

  constructor(private http: HttpClient) {}

  reqLogin(): Observable<{ access_token: string; }> {
    return this.http.post<{access_token: string}>(`/login`, 'test');
  }

  reqRegister(): Observable<{ access_token: string; }> {
    return this.http.post<{access_token: string}>(`/login`, 'test');
  }

}

app.module.ts

代码语言:javascript
运行
复制
import { AuthRestService } from './components/auth-service/auth-rest.service';


@NgModule({
  declarations: [
   ...
  ],
  imports: [
    ...
    AuthModule.forRoot(AuthRestService),
    ...
  ],
  providers: [AuthModule],
  bootstrap: [AppComponent]
})
export class AppModule { }

我无法创建AuthRestService的实例,因为该服务具有依赖关系(HttpClient)。有什么方法可以告诉我这个服务。

EN

回答 1

Stack Overflow用户

发布于 2021-07-13 09:35:20

这是可能的使用角的Injector

代码语言:javascript
运行
复制
import { Injector, ModuleWithProviders, NgModule, Optional, Provider, SkipSelf } from '@angular/core';
import { isFunction } from 'lodash';

export function resolveService(cfg: SharedConfig, inj: Injector): IncompleteService {
  const provider = cfg?.service;
  // if service is an angular provider, use Injector, otherwise return service instance as simple value
  const service = isFunction(service) ? inj.get(provider) : provider;
  return service;
}

/**
 * Service to be implemented from outside the module.
 */
@Injectable()
export abstract class IncompleteService {
  abstract strategyMethod();
}

// Optional: A config object is optional of course, but usually it fits the needs.
export interface SharedConfig {
  service: IncompleteService | Type<IncompleteService> | InjectionToken<IncompleteService>;
  // other config properties...
}

/*
 * Optional: If a Config interface is used, one might resolve the config itself 
 * using other dependencies (e.g. load JSON via HTTPClient). Hence an InjectionToken 
 * is necessary.
 */
export const SHARED_CONFIG = new InjectionToken<SharedConfig>('shared-config');

// Optional: If SharedConfig is resolved with dependencies, it must be provided itself.  
export type ModuleConfigProvider = ValueProvider | ClassProvider | ExistingProvider | FactoryProvider;

/**
 * One can provide the config as is, i.e. "{ service: MyService }" or resolved by 
 * injection, i.e.
 * { provide: SHARED_CONFIG: useFactory: myConfigFactory, deps: [DependentService1, DependentService2] }
 */
@NgModule({
  declarations: [],
  imports: []
})
export class SharedModule {
  static forRoot(config: SharedConfig | ModuleConfigProvider): ModuleWithProviders<SharedModule> {
    // dynamic (config is Provider) or simple (config is SharedConfig)
    return {
      ngModule: SharedModule,
      providers: [
        (config as ModuleConfigProvider).provide ? (config as Provider) : { provide: SHARED_CONFIG, useValue: config },
        { provide: IncompleteService, useFactory: resolveService, deps: [SHARED_CONFIG, Injector] },
        // ... provide additional things
      ],
    };
}


/**
 * In general not really useful, because usually an instance of IncompleteService
 * need other dependencies itself. Hence you cannot provide this instance without
 * creating it properly. But for the sake of completeness, it should work as well.
 */
@NgModule({
  declarations: [],
  imports: []
})
export class MostSimpleSharedModule {
  static forRoot(service: IncompleteService): ModuleWithProviders<SharedModule> {
    // dynamic (config is Provider) or simple (config is SharedConfig)
    return {
      ngModule: SharedModule,
      providers: [
        { provide: IncompleteService, useValue: service },
        // ... provide additional things
      ],
    };
}

编辑

如果你真的需要一个接口。一个(可注入的)抽象类IncompleteService,只需定义另一个InjectionToken<IncompleteServiceInterface>并显式提供此令牌即可。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62412960

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档