如何在基于组件的提供程序中即时更改服务类实现?

时间:2019-10-16 10:04:41

标签: angular dependency-injection angular-providers

我还是Angular的新手。当前,我在一个组件中有一个基于一个抽象类的REST服务的两种实现。就像scenario一样,唯一的区别是我在组件级别注入了服务:

@Component({
  selector: 'this-is-tag',
  template: htmlStr,
  providers: [{ provide: RESTToken, useClass: FirstRESTService }],
  ...

问题是,我可以基于特定配置在运行时切换实现吗?或者,对于这个问题,我应该使用其他方法吗?

编辑:

似乎我需要使用接口,而不是抽象类。 link

2 个答案:

答案 0 :(得分:0)

解决方案是用useFactory代替UseClass,如下例所示更改代码: 首先,创建一个具有DI逻辑决定的工厂类:

export function myFactory() {
  if(myRule){
       return new FirstRESTService();
    }
  return new  SecondRESTService();
}

然后将您的组件代码更改为:

@Component({
  selector: 'this-is-tag',
  template: htmlStr,
  providers: [{ provide: RESTToken, useFactory: myFactory}],

有时会出现这种情况,您想向工厂添加一些依赖项,因为您需要它来决定是返回serviceA还是serviceB。 为了满足这个要求,我们可以使用如下的deps属性:

export function myFactory(obj:MyDependencyClass) {
  if(obj.HasMyRule){
       return new FirstRESTService();
    }
  return new  SecondRESTService();
}
另外,在您的组件中:

@Component({
  selector: 'this-is-tag',
  template: htmlStr,
  providers: [{ provide: RESTToken, useFactory: myFactory}],
  deps: [MyDependencyClass]

但不要忘记在app.module.ts中包含MyDependencyClass。

答案 1 :(得分:0)

因此,我最终使用接口而不是该解决方案的抽象类。而且,当我尝试对两个服务(例如this question)使用基类时,继承方法也会失败。

这是我在JIT和AOT编译中的可行解决方案:

@Component({
  selector: 'invoice-list',
  templateUrl: './invoice-list.component.html',
  providers: [{ provide: RESTToken, useClass: InvoiceRESTService }],
  ...

组件中实际使用的服务:

@Injectable()
export class InvoiceRESTService {

    public invoiceType: InvoiceType;

    constructor(
        private _visma: VismaRESTService,
        private _fortnox: FortnoxRESTService
    ) {}

    public get instance(): RESTServiceInterface {

        if (this.invoiceType === InvoiceType.Visma) {
            return this._visma;
        } else if (this.invoiceType === InvoiceType.Fortnox) {
            return this._fortnox;
        } else {
            console.log('Please set the invoice type.');
        }
    }

}

界面本身:

export interface RESTServiceInterface {

  getExportDataFunctionURL(): string;

  // So far, only for Visma
  getAuthUrl?(): string;

  getButtonTitlle(): string;

  synchData(instanceUid: string, code?: string): Observable<Object>;

  updateInvoice(payload: any): Observable<Object>;

  // Loading messages
  getWaitingMessage(): string;

  getSuccessMessage(): string;

  getErrorMessage(code: VismaErrorCode | FortnoxErrorCode | BKErrorCode): string;

}

实现接口的服务:

@Injectable({
  providedIn: 'root'
})

export class FortnoxRESTService implements RESTServiceInterface {
   ...
   ...
   ...
}


@Injectable({
    providedIn: 'root'
})

export class VismaRESTService implements RESTServiceInterface {
   ...
   ...
   ...
}