【问题标题】:How to alternate service class implementation on-the-fly in component based provider?如何在基于组件的提供者中即时替代服务类实现?
【发布时间】:2019-10-16 10:04:41
【问题描述】:

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

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

问题是,我可以根据特定配置在运行时切换实现吗?或者,我应该对这个问题使用不同的方法吗?

编辑:

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

【问题讨论】:

    标签: angular dependency-injection angular-providers


    【解决方案1】:

    解决方案是 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。

    【讨论】:

    • 说实话,比起这种工厂方式,我更喜欢接口解决方案。这两种解决方案各有优劣吗?
    【解决方案2】:

    所以,我最终使用了接口,而不是这个解决方案的抽象类。而且,当我尝试为我的两个服务使用基类时,继承方法失败了,比如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 {
       ...
       ...
       ...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多