-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(example): add examples of effects not based on the Actions stream (
#1845) As suggested, added two examples of effects not based on Actions stream: - listen for router navigation events and update page title - log out the user after a specified period of inactivity Closes #1830
- Loading branch information
1 parent
d874cfc
commit 3454e70
Showing
12 changed files
with
197 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import * as LayoutActions from './layout.actions'; | ||
import * as UserActions from './user.actions'; | ||
|
||
export { LayoutActions }; | ||
export { LayoutActions, UserActions }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { createAction } from '@ngrx/store'; | ||
|
||
export const idleTimeout = createAction('[User] Idle Timeout'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './user.effects'; | ||
export * from './router.effects'; |
41 changes: 41 additions & 0 deletions
41
projects/example-app/src/app/core/effects/router.effects.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router'; | ||
import { Title } from '@angular/platform-browser'; | ||
import { TestBed } from '@angular/core/testing'; | ||
|
||
import { of } from 'rxjs'; | ||
|
||
import { RouterEffects } from '@example-app/core/effects'; | ||
|
||
describe('RouterEffects', () => { | ||
let effects: RouterEffects; | ||
let titleService: Title; | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
providers: [ | ||
RouterEffects, | ||
{ | ||
provide: Router, | ||
useValue: { events: of(new NavigationEnd(1, '', '')) }, | ||
}, | ||
{ | ||
provide: ActivatedRoute, | ||
useValue: { data: of({ title: 'Search' }) }, | ||
}, | ||
{ provide: Title, useValue: { setTitle: jest.fn() } }, | ||
], | ||
}); | ||
|
||
effects = TestBed.get(RouterEffects); | ||
titleService = TestBed.get(Title); | ||
}); | ||
|
||
describe('updateTitle$', () => { | ||
it('should update the title on router navigation', () => { | ||
effects.updateTitle$.subscribe(); | ||
expect(titleService.setTitle).toHaveBeenCalledWith( | ||
'Book Collection - Search' | ||
); | ||
}); | ||
}); | ||
}); |
34 changes: 34 additions & 0 deletions
34
projects/example-app/src/app/core/effects/router.effects.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { Injectable } from '@angular/core'; | ||
import { Router, NavigationEnd, ActivatedRoute } from '@angular/router'; | ||
import { Title } from '@angular/platform-browser'; | ||
|
||
import { tap, filter, map, mergeMap } from 'rxjs/operators'; | ||
|
||
import { createEffect } from '@ngrx/effects'; | ||
|
||
@Injectable() | ||
export class RouterEffects { | ||
updateTitle$ = createEffect( | ||
() => | ||
this.router.events.pipe( | ||
filter(event => event instanceof NavigationEnd), | ||
map(() => { | ||
let route = this.activatedRoute; | ||
while (route.firstChild) route = route.firstChild; | ||
return route; | ||
}), | ||
mergeMap(route => route.data), | ||
map(data => `Book Collection - ${data['title']}`), | ||
tap(title => this.titleService.setTitle(title)) | ||
), | ||
{ | ||
dispatch: false, | ||
} | ||
); | ||
|
||
constructor( | ||
private router: Router, | ||
private titleService: Title, | ||
private activatedRoute: ActivatedRoute | ||
) {} | ||
} |
65 changes: 65 additions & 0 deletions
65
projects/example-app/src/app/core/effects/user.effects.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { Action } from '@ngrx/store'; | ||
import { TestBed, fakeAsync, tick } from '@angular/core/testing'; | ||
|
||
import { UserEffects } from '@example-app/core/effects'; | ||
import { UserActions } from '@example-app/core/actions'; | ||
|
||
describe('UserEffects', () => { | ||
let effects: UserEffects; | ||
const eventsMap: { [key: string]: any } = {}; | ||
|
||
beforeAll(() => { | ||
document.addEventListener = jest.fn((event, cb) => { | ||
eventsMap[event] = cb; | ||
}); | ||
}); | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
providers: [UserEffects], | ||
}); | ||
|
||
effects = TestBed.get(UserEffects); | ||
}); | ||
|
||
describe('idle$', () => { | ||
it( | ||
'should trigger idleTimeout action after 5 minutes', | ||
fakeAsync(() => { | ||
let action: Action | undefined; | ||
effects.idle$.subscribe(res => (action = res)); | ||
|
||
// Initial action to trigger the effect | ||
eventsMap['click'](); | ||
|
||
tick(2 * 60 * 1000); | ||
expect(action).toBeUndefined(); | ||
|
||
tick(3 * 60 * 1000); | ||
expect(action).toBeDefined(); | ||
expect(action!.type).toBe(UserActions.idleTimeout.type); | ||
}) | ||
); | ||
|
||
it( | ||
'should reset timeout on user activity', | ||
fakeAsync(() => { | ||
let action: Action | undefined; | ||
effects.idle$.subscribe(res => (action = res)); | ||
|
||
// Initial action to trigger the effect | ||
eventsMap['keydown'](); | ||
|
||
tick(4 * 60 * 1000); | ||
eventsMap['mousemove'](); | ||
|
||
tick(4 * 60 * 1000); | ||
expect(action).toBeUndefined(); | ||
|
||
tick(1 * 60 * 1000); | ||
expect(action).toBeDefined(); | ||
expect(action!.type).toBe(UserActions.idleTimeout.type); | ||
}) | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { Injectable, Inject } from '@angular/core'; | ||
|
||
import { fromEvent, merge, timer } from 'rxjs'; | ||
import { map, switchMapTo } from 'rxjs/operators'; | ||
|
||
import { createEffect } from '@ngrx/effects'; | ||
import { UserActions } from '@example-app/core/actions'; | ||
|
||
@Injectable() | ||
export class UserEffects { | ||
clicks$ = fromEvent(document, 'click'); | ||
keys$ = fromEvent(document, 'keydown'); | ||
mouse$ = fromEvent(document, 'mousemove'); | ||
|
||
idle$ = createEffect(() => | ||
merge(this.clicks$, this.keys$, this.mouse$).pipe( | ||
switchMapTo(timer(5 * 60 * 1000)), // 5 minute inactivity timeout | ||
map(() => UserActions.idleTimeout()) | ||
) | ||
); | ||
} |