Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions modules/effects/spec/integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { mapTo, exhaustMap, tap } from 'rxjs/operators';
import { Observable } from 'rxjs';

describe('NgRx Effects Integration spec', () => {
it('throws if forRoot() is used more than once', (done: any) => {
it('throws if forRoot() with Effects is used more than once', (done: any) => {
TestBed.configureTestingModule({
imports: [
StoreModule.forRoot({}),
Expand All @@ -45,6 +45,30 @@ describe('NgRx Effects Integration spec', () => {
});
});

it('does not throw if forRoot() is used more than once with empty effects', (done: any) => {
TestBed.configureTestingModule({
imports: [
StoreModule.forRoot({}),
EffectsModule.forRoot([]),
RouterTestingModule.withRoutes([]),
],
});

let router: Router = TestBed.inject(Router);
const loader: SpyNgModuleFactoryLoader = TestBed.inject(
NgModuleFactoryLoader
) as SpyNgModuleFactoryLoader;

// empty forRoot([]) 👇
loader.stubbedModules = { feature: FeatModuleWithEmptyForRoot };
router.resetConfig([{ path: 'feature-path', loadChildren: 'feature' }]);

router.navigateByUrl('/feature-path').then(() => {
// success
done();
});
});

describe('actions', () => {
const createDispatchedReducer = (dispatchedActions: string[] = []) => (
state = {},
Expand Down Expand Up @@ -386,11 +410,19 @@ describe('NgRx Effects Integration spec', () => {
}
}

@Injectable()
class SomeEffect {}

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

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

@NgModule({
imports: [
EffectsModule.forFeature([FeatEffectFromLazyLoadedModuleWithInitAction]),
Expand Down
23 changes: 16 additions & 7 deletions modules/effects/src/effects_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ModuleWithProviders,
NgModule,
Optional,
Self,
SkipSelf,
Type,
} from '@angular/core';
Expand Down Expand Up @@ -57,11 +58,6 @@ export class EffectsModule {
return {
ngModule: EffectsRootModule,
providers: [
{
provide: _ROOT_EFFECTS_GUARD,
useFactory: _provideForRootGuard,
deps: [[EffectsRunner, new Optional(), new SkipSelf()]],
},
{
provide: EFFECTS_ERROR_HANDLER,
useValue: defaultEffectsErrorHandler,
Expand All @@ -74,6 +70,14 @@ export class EffectsModule {
provide: _ROOT_EFFECTS,
useValue: [rootEffects],
},
{
provide: _ROOT_EFFECTS_GUARD,
useFactory: _provideForRootGuard,
deps: [
[EffectsRunner, new Optional(), new SkipSelf()],
[_ROOT_EFFECTS, new Self()],
],
},
{
provide: USER_PROVIDED_EFFECTS,
multi: true,
Expand Down Expand Up @@ -114,8 +118,13 @@ export function createEffectInstances(
return effects.map((effect) => injector.get(effect));
}

export function _provideForRootGuard(runner: EffectsRunner): any {
if (runner) {
export function _provideForRootGuard(
runner: EffectsRunner,
rootEffects: any[][]
): any {
// check whether any effects are actually passed
const hasEffects = !(rootEffects.length === 1 && rootEffects[0].length === 0);
if (hasEffects && runner) {
throw new TypeError(
`EffectsModule.forRoot() called twice. Feature modules should use EffectsModule.forFeature() instead.`
);
Expand Down