ngrx EffectsModule-需要帮助以了解提供者的用法

时间:2018-07-08 18:09:55

标签: angular ngrx-effects

我试图了解Angular中提供者工厂的使用。 我了解Angular Documentation中提供的示例。

但是,我在ngrx EffectsModule (effects_module.ts)中遇到了提供者工厂的一种非常不寻常的用法。

@NgModule({})
export class EffectsModule {
  static forFeature(featureEffects: Type<any>[]): ModuleWithProviders {
    return {
      ngModule: EffectsFeatureModule,
      providers: [
        featureEffects,
        {
          provide: FEATURE_EFFECTS,
          multi: true,
          deps: featureEffects,
          useFactory: createSourceInstances,
        },
      ],
    };
  }

  // *** <snip> forRoot method for brevity

}
export function createSourceInstances(...instances: any[]) {
  return instances;
}

我了解multi:true的目的。

但是,我很难弄清楚为什么featureEffects会被声明为提供者,并被用作FEATURE_EFFECTS的依赖项-createSourceInstances(...instances: any[])会实现什么?

此模式是什么,在哪里可以使用?

1 个答案:

答案 0 :(得分:5)

让我们从@ngrx/effects documentation开始。

我们需要做的第一件事就是创建一个AuthEffects服务

@Injectable()
export class AuthEffects {
  @Effect()
  ...

  constructor(private http: HttpClient, private actions$: Actions) {}
}

我们如何注册效果?

很简单:

EffectsModule.forRoot([AuthEffects])

EffectsModule.forFeature([AuthEffects])

现在,让我们在这里停止并思考AuthEffects是什么。

AuthEffects只是普通的角度服务,可以具有任何依赖关系(在这种情况下为HttpClientActions),这些依赖关系将通过Angular DI系统来解决。

我们将这项服务传递给EffectsModule,还可以创建更多效果:

EffectsModule.forFeature([AuthEffects, MySecondEffects, ...])

现在,让我们假设我们是EffectsModule的作者。

我们为用户提供了仅通过创建角度服务即可提供任何数量的效果的机会。我们将在我们的图书馆中use进行这些服务:

addEffects(effectSourceInstance: any) {
    this.sources.addEffects(effectSourceInstance);
}

正如您所看到的,我们不仅需要类的实例,还需要我们提供的服务的实例。

我们如何创建这些实例?

也许是这样吗?

EffectsModule.forFeature([new AuthEffects(new HttpClient(...), new Actions(...))])

当然不是!我们可以让角度DI来做到这一点:

EffectsModule.forFeature([AuthEffects])
...

forFeature(featureEffects: Type<any>[]) {
  ....
    providers: [
        featureEffects,

现在,Angular DI知道所有这些服务以及如何创建它们。但是我作为作者也想在我的服务中使用它们,但是我不知道如何获得它们...

@NgModule({})
export class EffectsFeatureModule {
  constructor(
    // i need to get all effects provided by users but they are hidden in DI system
    ...
  ) {

  }
}

幸运的是,我们可以提供一个令牌,该令牌将在内部使用,并且库的作者将知道该令牌。

为此,我们使用creating InjectionToken:

export const FEATURE_EFFECTS = new InjectionToken<any[][]>(
  'ngrx/effects: Feature Effects'
);

并定义FEATURE_EFFECTS令牌的配方:

  featureEffects,
  {
    provide: FEATURE_EFFECTS,
    multi: true,
    deps: featureEffects,
    useFactory: (...instances: any[]) => {
       return instances;
    },
  },

multi告诉我们,可以多次定义此令牌(即,我们有多个forFeatures调用)

deps指定FEATURE_EFFECTS令牌将使用由Angular DI创建的所有featureEffects 实例

useFactory将这些实例作为参数。

通过这种方式,我们确切地知道注入FEATURE_EFFECTS令牌会为我们提供所有效果实例。

让我们考虑是否可以省略featureEffects,例如:

{
  provide: FEATURE_EFFECTS,
  multi: true,
  useValue: featureEffects
},

如果这样做,我们将不会获得实例,而只会得到类(函数)的数组。但是我们需要所有 instantiized 类,这里只有Angular DI是最好的朋友。

最后,尽管Angular DI是非常强大的模式,但它也有一些限制。

在AOT中,以下代码:

  featureEffects,
  {
    provide: FEATURE_EFFECTS,
    multi: true,
    deps: featureEffects,
    useFactory: (...instances: any[]) => {
       return instances;
    },
  },

将导致错误:

  

在装饰器中不支持函数表达式   '效果模块'   考虑将函数表达式更改为导出的函数。

所以这就是为什么写如下:

providers: [
  featureEffects,
  {
     provide: FEATURE_EFFECTS,
     multi: true,
     deps: featureEffects,
     useFactory: createSourceInstances,
   },
],
...
export function createSourceInstances(...instances: any[]) {
  return instances;
}

我们可以成功获取这些实例inside the library

@NgModule({})
export class EffectsFeatureModule {
  constructor(
    ...
    @Inject(FEATURE_EFFECTS) effectSourceGroups: any[][],
...