Skip to content

Commit

Permalink
feat(Effects): dispatch init feature effects action on init
Browse files Browse the repository at this point in the history
  • Loading branch information
timdeschryver committed Aug 28, 2018
1 parent ba8d300 commit 0e4cdd2
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 22 deletions.
74 changes: 63 additions & 11 deletions modules/effects/spec/effects_feature_module.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Injectable, NgModule } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { combineLatest } from 'rxjs';
import {
Action,
createFeatureSelector,
Expand All @@ -8,25 +9,39 @@ import {
Store,
StoreModule,
} from '@ngrx/store';
import { Observable } from 'rxjs';
import { map, withLatestFrom } from 'rxjs/operators';

import { map, withLatestFrom, filter } from 'rxjs/operators';
import { Actions, Effect, EffectsModule } from '../';
import { EffectsFeatureModule } from '../src/effects_feature_module';
import {
EffectsFeatureModule,
FEATURE_EFFECTS_INIT,
FeatureEffectsInit,
} from '../src/effects_feature_module';
import { EffectsRootModule } from '../src/effects_root_module';
import { FEATURE_EFFECTS } from '../src/tokens';

describe('Effects Feature Module', () => {
describe('when registered', () => {
const sourceA = 'sourceA';
const sourceB = 'sourceB';
const sourceC = 'sourceC';
const effectSourceGroups = [[sourceA], [sourceB], [sourceC]];
class SourceA {}
class SourceB {}
class SourceC {}

const sourceA = new SourceA();
const sourceB = new SourceB();
const sourceC = new SourceC();

const effectSourceGroups = [[sourceA], [sourceB, sourceC]];
let mockEffectSources: { addEffects: jasmine.Spy };
let mockStore: { dispatch: jasmine.Spy };

beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{
provide: Store,
useValue: {
dispatch: jasmine.createSpy('dispatch'),
},
},
{
provide: EffectsRootModule,
useValue: {
Expand All @@ -42,6 +57,7 @@ describe('Effects Feature Module', () => {
});

mockEffectSources = TestBed.get(EffectsRootModule);
mockStore = TestBed.get(Store);
});

it('should add all effects when instantiated', () => {
Expand All @@ -51,11 +67,24 @@ describe('Effects Feature Module', () => {
expect(mockEffectSources.addEffects).toHaveBeenCalledWith(sourceB);
expect(mockEffectSources.addEffects).toHaveBeenCalledWith(sourceC);
});

it('should dispatch feature effect init actions when instantiated', () => {
TestBed.get(EffectsFeatureModule);

expect(mockStore.dispatch).toHaveBeenCalledWith({
type: FEATURE_EFFECTS_INIT,
effects: ['SourceA'],
});

expect(mockStore.dispatch).toHaveBeenCalledWith({
type: FEATURE_EFFECTS_INIT,
effects: ['SourceB', 'SourceC'],
});
});
});

describe('when registered in a different NgModule from the feature state', () => {
let effects: FeatureEffects;
let actions$: Observable<any>;
let store: Store<any>;

beforeEach(() => {
Expand All @@ -77,8 +106,12 @@ describe('Effects Feature Module', () => {

store.dispatch(action);

store.pipe(select(getDataState)).subscribe(res => {
expect(res).toBe(110);
combineLatest(
store.pipe(select(getDataState)),
store.pipe(select(getInitialized))
).subscribe(([data, initialized]) => {
expect(data).toBe(110);
expect(initialized).toBe(true);
done();
});
});
Expand All @@ -93,16 +126,25 @@ interface State {

interface DataState {
data: number;
initialized: boolean;
}

const initialState: DataState = {
data: 100,
initialized: false,
};

function reducer(state: DataState = initialState, action: Action) {
switch (action.type) {
case 'INITIALIZE_FEATURE': {
return {
...state,
initialized: true,
};
}
case 'INCREASE':
return {
...state,
data: state.data + 10,
};
}
Expand All @@ -112,11 +154,21 @@ function reducer(state: DataState = initialState, action: Action) {
const getFeatureState = createFeatureSelector<DataState>(FEATURE_KEY);

const getDataState = createSelector(getFeatureState, state => state.data);
const getInitialized = createSelector(
getFeatureState,
state => state.initialized
);

@Injectable()
class FeatureEffects {
constructor(private actions: Actions, private store: Store<State>) {}

@Effect()
init = this.actions.ofType<FeatureEffectsInit>(FEATURE_EFFECTS_INIT).pipe(
filter(action => action.effects.includes('FeatureEffects')),
map(action => ({ type: 'INITIALIZE_FEATURE' }))
);

@Effect()
effectWithStore = this.actions.ofType('INCREMENT').pipe(
withLatestFrom(this.store.select(getDataState)),
Expand Down
38 changes: 27 additions & 11 deletions modules/effects/src/effects_feature_module.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
import { NgModule, Inject, Optional } from '@angular/core';
import { StoreRootModule, StoreFeatureModule } from '@ngrx/store';
import { NgModule, Inject } from '@angular/core';
import { Store } from '@ngrx/store';
import { EffectsRootModule } from './effects_root_module';
import { FEATURE_EFFECTS } from './tokens';
import { getSourceForInstance } from './effects_metadata';

export const FEATURE_EFFECTS_INIT = '@ngrx/feature-effects/init';
export type FeatureEffectsInit = {
type: typeof FEATURE_EFFECTS_INIT;
effects: string[];
};

@NgModule({})
export class EffectsFeatureModule {
constructor(
private root: EffectsRootModule,
@Inject(FEATURE_EFFECTS) effectSourceGroups: any[][],
@Optional() storeRootModule: StoreRootModule,
@Optional() storeFeatureModule: StoreFeatureModule
root: EffectsRootModule,
store: Store<any>,
@Inject(FEATURE_EFFECTS) effectSourceGroups: any[][]
) {
effectSourceGroups.forEach(group =>
group.forEach(effectSourceInstance =>
root.addEffects(effectSourceInstance)
)
);
effectSourceGroups.forEach(group => {
let effectSourceNames: string[] = [];

group.forEach(effectSourceInstance => {
root.addEffects(effectSourceInstance);

const { constructor } = getSourceForInstance(effectSourceInstance);
effectSourceNames.push(constructor.name);
});

store.dispatch(<FeatureEffectsInit>{
type: FEATURE_EFFECTS_INIT,
effects: effectSourceNames,
});
});
}
}
4 changes: 4 additions & 0 deletions modules/effects/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ export { EffectSources } from './effect_sources';
export { OnRunEffects } from './on_run_effects';
export { EffectNotification } from './effect_notification';
export { ROOT_EFFECTS_INIT } from './effects_root_module';
export {
FEATURE_EFFECTS_INIT,
FeatureEffectsInit,
} from './effects_feature_module';

0 comments on commit 0e4cdd2

Please sign in to comment.