Skip to content

Commit

Permalink
feat(effects): add user provided effects to EffectsModule
Browse files Browse the repository at this point in the history
  • Loading branch information
Leon Marzahn committed Jan 17, 2020
1 parent de9a590 commit ac9231e
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 14 deletions.
45 changes: 40 additions & 5 deletions modules/effects/spec/integration.spec.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { NgModuleFactoryLoader, NgModule } 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 @@ -95,6 +96,21 @@ describe('NgRx Effects Integration spec', () => {
});
});

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

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

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

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

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

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

@NgModule({
imports: [EffectsModule.forFeature()],
providers: [
FeatUserProvidedEffect,
{
provide: USER_PROVIDED_FEATURE_EFFECTS,
multi: true,
useValue: [FeatUserProvidedEffect],
},
],
})
class FeatModuleWithUserProvidedEffects {}
});
64 changes: 55 additions & 9 deletions modules/effects/src/effects_module.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,56 @@
import {
NgModule,
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,
_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';
import { EffectsRunner } from './effects_runner';

@NgModule({})
export class EffectsModule {
static forFeature(
featureEffects: Type<any>[]
featureEffects: Type<any>[] = []
): ModuleWithProviders<EffectsFeatureModule> {
return {
ngModule: EffectsFeatureModule,
providers: [
featureEffects,
{
provide: USER_PROVIDED_FEATURE_EFFECTS,
multi: true,
useValue: [],
},
{
provide: _FEATURE_EFFECTS,
useValue: featureEffects,
},
{
provide: FEATURE_EFFECTS,
multi: true,
deps: featureEffects,
useFactory: createSourceInstances,
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 @@ -46,16 +64,44 @@ 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 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;
}
Expand Down
5 changes: 5 additions & 0 deletions modules/effects/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ export { EffectNotification } from './effect_notification';
export {
ROOT_EFFECTS_INIT,
rootEffectsInit,
EffectsRootModule,
} 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';
12 changes: 12 additions & 0 deletions modules/effects/src/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,21 @@ 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 USER_PROVIDED_FEATURE_EFFECTS = new InjectionToken<Type<any>[][]>(
'ngrx/effects: User Provided Feature Effects'
);
export const _FEATURE_EFFECTS = new InjectionToken<Type<any>[]>(
'ngrx/effects: Internal Feature Effects'
);
export const FEATURE_EFFECTS = new InjectionToken<any[][]>(
'ngrx/effects: Feature Effects'
);

0 comments on commit ac9231e

Please sign in to comment.