Skip to content

Commit

Permalink
feat(effects): add user provided effects to EffectsModule
Browse files Browse the repository at this point in the history
feat(effects): add user provided effects to EffectsModule
feat(effects): add user provided effects to EffectsModule
  • Loading branch information
Leon Marzahn committed Jan 16, 2020
1 parent ebf724f commit cbccef2
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 74 deletions.
41 changes: 17 additions & 24 deletions modules/effects/spec/integration.spec.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import {
NgModuleFactoryLoader,
NgModule,
InjectionToken,
Type,
} from '@angular/core';
import { NgModule, NgModuleFactoryLoader } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import {
RouterTestingModule,
SpyNgModuleFactoryLoader,
} from '@angular/router/testing';
import { Router } from '@angular/router';
import { Store, Action } from '@ngrx/store';
import { Action, Store } from '@ngrx/store';
import {
EffectsModule,
EffectSources,
OnIdentifyEffects,
OnInitEffects,
ROOT_EFFECTS_INIT,
OnIdentifyEffects,
EffectSources,
USER_PROVIDED_FEATURE_EFFECTS,
} from '..';

describe('NgRx Effects Integration spec', () => {
Expand Down Expand Up @@ -100,16 +96,16 @@ describe('NgRx Effects Integration spec', () => {
});
});

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

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

router.navigateByUrl('/feature-path').then(() => {
expect(dispatch).toHaveBeenCalledWith({
type: '[FeatEffectWithInjection]: INIT',
type: '[FeatUserProvidedEffect]: INIT',
});
done();
});
Expand Down Expand Up @@ -153,30 +149,27 @@ describe('NgRx Effects Integration spec', () => {
constructor(private effectIdentifier: string) {}
}

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

@NgModule({
imports: [EffectsModule.forRoot([])],
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)],
imports: [EffectsModule.forFeature()],
providers: [
FeatEffectWithInjection,
FeatUserProvidedEffect,
{
provide: FEATURE_INJECTED_EFFECTS_TOKEN,
useValue: [FeatEffectWithInjection],
provide: USER_PROVIDED_FEATURE_EFFECTS,
multi: true,
useValue: [FeatUserProvidedEffect],
},
],
})
class FeatModuleWithInjectedEffects {}
class FeatModuleWithUserProvidedEffects {}
});
82 changes: 40 additions & 42 deletions modules/effects/src/effects_module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import {
Inject,
InjectionToken,
Injector,
ModuleWithProviders,
NgModule,
Expand All @@ -12,10 +10,12 @@ import { EffectSources } from './effect_sources';
import { Actions } from './actions';
import {
_FEATURE_EFFECTS,
_FEATURE_EFFECTS_TOKEN,
_ROOT_EFFECTS,
_ROOT_EFFECTS_GUARD,
FEATURE_EFFECTS,
ROOT_EFFECTS,
USER_PROVIDED_FEATURE_EFFECTS,
USER_PROVIDED_ROOT_EFFECTS,
} from './tokens';
import { EffectsFeatureModule } from './effects_feature_module';
import { EffectsRootModule } from './effects_root_module';
Expand All @@ -24,37 +24,33 @@ import { EffectsRunner } from './effects_runner';
@NgModule({})
export class EffectsModule {
static forFeature(
featureEffects: Type<any>[] | InjectionToken<Type<any>[]>
featureEffects: Type<any>[] = []
): ModuleWithProviders<EffectsFeatureModule> {
return {
ngModule: EffectsFeatureModule,
providers: [
featureEffects instanceof InjectionToken ? [] : featureEffects,
{ provide: _FEATURE_EFFECTS, multi: true, useValue: featureEffects },
featureEffects,
{
provide: _FEATURE_EFFECTS_TOKEN,
provide: USER_PROVIDED_FEATURE_EFFECTS,
multi: true,
useExisting:
featureEffects instanceof InjectionToken
? featureEffects
: _FEATURE_EFFECTS,
useValue: [],
},
{
provide: _FEATURE_EFFECTS,
useValue: featureEffects,
},
{
provide: FEATURE_EFFECTS,
multi: true,
deps: [
Injector,
_FEATURE_EFFECTS,
[new Inject(_FEATURE_EFFECTS_TOKEN)],
],
useFactory: _createFeatureEffects,
useFactory: createEffects,
deps: [Injector, _FEATURE_EFFECTS, USER_PROVIDED_FEATURE_EFFECTS],
},
],
};
}

static forRoot(
rootEffects: Type<any>[]
rootEffects: Type<any>[] = []
): ModuleWithProviders<EffectsRootModule> {
return {
ngModule: EffectsRootModule,
Expand All @@ -68,27 +64,48 @@ export class EffectsModule {
EffectSources,
Actions,
rootEffects,
{
provide: USER_PROVIDED_ROOT_EFFECTS,
multi: true,
useValue: [],
},
{
provide: _ROOT_EFFECTS,
useValue: rootEffects,
},
{
provide: ROOT_EFFECTS,
deps: rootEffects,
useFactory: createSourceInstances,
useFactory: createEffects,
deps: [Injector, _ROOT_EFFECTS, USER_PROVIDED_ROOT_EFFECTS],
},
],
};
}
}

export function createSourceInstances(...instances: any[]) {
return instances;
export function createEffects(
injector: Injector,
effects: Type<any>[],
userProvidedEffectGroups: Type<any>[][]
): any[] {
const mergedEffects: Type<any>[] = effects;
userProvidedEffectGroups.forEach(group => mergedEffects.push(...group));
return createSourceInstances(
...createEffectInstances(injector, ...mergedEffects)
);
}

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

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

export function _provideForRootGuard(runner: EffectsRunner): any {
if (runner) {
throw new TypeError(
Expand All @@ -97,22 +114,3 @@ 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));
}
9 changes: 5 additions & 4 deletions modules/effects/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ export { Actions, ofType } from './actions';
export { EffectsModule } from './effects_module';
export { EffectSources } from './effect_sources';
export { EffectNotification } from './effect_notification';
export {
ROOT_EFFECTS_INIT,
rootEffectsInit,
} from './effects_root_module';
export { ROOT_EFFECTS_INIT, rootEffectsInit } from './effects_root_module';
export { act } from './act';
export {
OnIdentifyEffects,
OnRunEffects,
OnInitEffects,
} from './lifecycle_hooks';
export {
USER_PROVIDED_ROOT_EFFECTS,
USER_PROVIDED_FEATURE_EFFECTS,
} from './tokens';
14 changes: 10 additions & 4 deletions modules/effects/src/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@ export const _ROOT_EFFECTS_GUARD = new InjectionToken<void>(
export const IMMEDIATE_EFFECTS = new InjectionToken<any[]>(
'ngrx/effects: Immediate Effects'
);
export const USER_PROVIDED_ROOT_EFFECTS = new InjectionToken<Type<any>>(
'ngrx/effects: User Provided Root Effects'
);
export const _ROOT_EFFECTS = new InjectionToken<Type<any>[]>(
'ngrx/effects: Internal Root Effects'
);
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 USER_PROVIDED_FEATURE_EFFECTS = new InjectionToken<Type<any>[]>(
'ngrx/effects: User Provided Feature Effects'
);
export const _FEATURE_EFFECTS_TOKEN = new InjectionToken<Type<any>[][]>(
'ngrx/effects: Internal Feature Effects Token'
export const _FEATURE_EFFECTS = new InjectionToken<Type<any>[]>(
'ngrx/effects: Internal Feature Effects'
);
export const FEATURE_EFFECTS = new InjectionToken<any[][]>(
'ngrx/effects: Feature Effects'
Expand Down

0 comments on commit cbccef2

Please sign in to comment.