Skip to content

Commit

Permalink
feat(effects): add injection token to EffectsModule.forFeature
Browse files Browse the repository at this point in the history
feat(effects): add injection token to EffectsModule.forFeature
  • Loading branch information
Leon Marzahn committed Nov 8, 2019
1 parent de9a590 commit ebf724f
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 8 deletions.
44 changes: 43 additions & 1 deletion modules/effects/spec/integration.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { NgModuleFactoryLoader, NgModule } from '@angular/core';
import {
NgModuleFactoryLoader,
NgModule,
InjectionToken,
Type,
} from '@angular/core';
import { TestBed } from '@angular/core/testing';
import {
RouterTestingModule,
Expand Down Expand Up @@ -95,6 +100,21 @@ describe('NgRx Effects Integration spec', () => {
});
});

it('should execute injected effects', (done: DoneFn) => {
let router: Router = TestBed.get(Router);
const loader: SpyNgModuleFactoryLoader = TestBed.get(NgModuleFactoryLoader);

loader.stubbedModules = { feature: FeatModuleWithInjectedEffects };
router.resetConfig([{ path: 'feature-path', loadChildren: 'feature' }]);

router.navigateByUrl('/feature-path').then(() => {
expect(dispatch).toHaveBeenCalledWith({
type: '[FeatEffectWithInjection]: INIT',
});
done();
});
});

class RootEffectWithInitAction implements OnInitEffects {
ngrxOnInitEffects(): Action {
return { type: '[RootEffectWithInitAction]: INIT' };
Expand Down Expand Up @@ -133,8 +153,30 @@ describe('NgRx Effects Integration spec', () => {
constructor(private effectIdentifier: string) {}
}

class FeatEffectWithInjection implements OnInitEffects {
ngrxOnInitEffects(): Action {
return { type: '[FeatEffectWithInjection]: INIT' };
}
}

@NgModule({
imports: [EffectsModule.forRoot([])],
})
class FeatModuleWithForRoot {}

const FEATURE_INJECTED_EFFECTS_TOKEN = new InjectionToken<Type<any>[]>(
'FEATURE_INJECTED_EFFECTS_TOKEN'
);

@NgModule({
imports: [EffectsModule.forFeature(FEATURE_INJECTED_EFFECTS_TOKEN)],
providers: [
FeatEffectWithInjection,
{
provide: FEATURE_INJECTED_EFFECTS_TOKEN,
useValue: [FeatEffectWithInjection],
},
],
})
class FeatModuleWithInjectedEffects {}
});
62 changes: 55 additions & 7 deletions modules/effects/src/effects_module.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,53 @@
import {
NgModule,
Inject,
InjectionToken,
Injector,
ModuleWithProviders,
Type,
NgModule,
Optional,
SkipSelf,
Type,
} from '@angular/core';
import { EffectSources } from './effect_sources';
import { Actions } from './actions';
import { ROOT_EFFECTS, FEATURE_EFFECTS, _ROOT_EFFECTS_GUARD } from './tokens';
import {
_FEATURE_EFFECTS,
_FEATURE_EFFECTS_TOKEN,
_ROOT_EFFECTS_GUARD,
FEATURE_EFFECTS,
ROOT_EFFECTS,
} from './tokens';
import { EffectsFeatureModule } from './effects_feature_module';
import { EffectsRootModule } from './effects_root_module';
import { EffectsRunner } from './effects_runner';

@NgModule({})
export class EffectsModule {
static forFeature(
featureEffects: Type<any>[]
featureEffects: Type<any>[] | InjectionToken<Type<any>[]>
): ModuleWithProviders<EffectsFeatureModule> {
return {
ngModule: EffectsFeatureModule,
providers: [
featureEffects,
featureEffects instanceof InjectionToken ? [] : featureEffects,
{ provide: _FEATURE_EFFECTS, multi: true, useValue: featureEffects },
{
provide: _FEATURE_EFFECTS_TOKEN,
multi: true,
useExisting:
featureEffects instanceof InjectionToken
? featureEffects
: _FEATURE_EFFECTS,
},
{
provide: FEATURE_EFFECTS,
multi: true,
deps: featureEffects,
useFactory: createSourceInstances,
deps: [
Injector,
_FEATURE_EFFECTS,
[new Inject(_FEATURE_EFFECTS_TOKEN)],
],
useFactory: _createFeatureEffects,
},
],
};
Expand Down Expand Up @@ -60,6 +82,13 @@ export function createSourceInstances(...instances: any[]) {
return instances;
}

export function createEffectInstances(
injector: Injector,
...effects: Type<any>[]
) {
return effects.map(effect => injector.get(effect));
}

export function _provideForRootGuard(runner: EffectsRunner): any {
if (runner) {
throw new TypeError(
Expand All @@ -68,3 +97,22 @@ export function _provideForRootGuard(runner: EffectsRunner): any {
}
return 'guarded';
}

export function _createFeatureEffects(
injector: Injector,
effectCollections: Type<any>[][] | InjectionToken<Type<any>[]>[]
): any[] {
const effects: Type<any>[] = [];

effectCollections.forEach(
(effectCollection: Type<any>[] | InjectionToken<Type<any>[]>) => {
effects.push(
...(effectCollection instanceof InjectionToken
? injector.get(effectCollection)
: effectCollection)
);
}
);

return createSourceInstances(...createEffectInstances(injector, ...effects));
}
6 changes: 6 additions & 0 deletions modules/effects/src/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ export const IMMEDIATE_EFFECTS = new InjectionToken<any[]>(
export const ROOT_EFFECTS = new InjectionToken<Type<any>[]>(
'ngrx/effects: Root Effects'
);
export const _FEATURE_EFFECTS = new InjectionToken<Type<any>[][]>(
'ngrx/effects: Internal Feature Effects'
);
export const _FEATURE_EFFECTS_TOKEN = new InjectionToken<Type<any>[][]>(
'ngrx/effects: Internal Feature Effects Token'
);
export const FEATURE_EFFECTS = new InjectionToken<any[][]>(
'ngrx/effects: Feature Effects'
);

0 comments on commit ebf724f

Please sign in to comment.