Skip to content

Commit 3454e70

Browse files
Adrian Fâciubrandonroberts
authored andcommitted
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
1 parent d874cfc commit 3454e70

File tree

12 files changed

+197
-6
lines changed

12 files changed

+197
-6
lines changed

projects/example-app/src/app/app-routing.module.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ export const routes: Routes = [
1111
loadChildren: '@example-app/books/books.module#BooksModule',
1212
canActivate: [AuthGuard],
1313
},
14-
{ path: '**', component: NotFoundPageComponent },
14+
{
15+
path: '**',
16+
component: NotFoundPageComponent,
17+
data: { title: 'Not found' },
18+
},
1519
];
1620

1721
@NgModule({

projects/example-app/src/app/app.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { ROOT_REDUCERS, metaReducers } from '@example-app/reducers';
1515

1616
import { CoreModule } from '@example-app/core';
1717
import { AppRoutingModule } from '@example-app/app-routing.module';
18+
import { UserEffects, RouterEffects } from '@example-app/core/effects';
1819
import { AppComponent } from '@example-app/core/containers';
1920

2021
@NgModule({
@@ -73,7 +74,7 @@ import { AppComponent } from '@example-app/core/containers';
7374
*
7475
* See: https://ngrx.io/guide/effects#registering-root-effects
7576
*/
76-
EffectsModule.forRoot([]),
77+
EffectsModule.forRoot([UserEffects, RouterEffects]),
7778
CoreModule,
7879
],
7980
bootstrap: [AppComponent],

projects/example-app/src/app/auth/auth-routing.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import { NgModule } from '@angular/core';
22
import { Routes, RouterModule } from '@angular/router';
33
import { LoginPageComponent } from '@example-app/auth/containers';
44

5-
const routes: Routes = [{ path: 'login', component: LoginPageComponent }];
5+
const routes: Routes = [
6+
{ path: 'login', component: LoginPageComponent, data: { title: 'Login' } },
7+
];
68

79
@NgModule({
810
imports: [RouterModule.forChild(routes)],

projects/example-app/src/app/auth/effects/auth.effects.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
import { Credentials } from '@example-app/auth/models';
1313
import { AuthService } from '@example-app/auth/services';
1414
import { LogoutConfirmationDialogComponent } from '@example-app/auth/components';
15+
import { UserActions } from '@example-app/core/actions';
1516

1617
@Injectable()
1718
export class AuthEffects {
@@ -69,6 +70,13 @@ export class AuthEffects {
6970
)
7071
);
7172

73+
logoutIdleUser$ = createEffect(() =>
74+
this.actions$.pipe(
75+
ofType(UserActions.idleTimeout),
76+
map(() => AuthActions.logout())
77+
)
78+
);
79+
7280
constructor(
7381
private actions$: Actions,
7482
private authService: AuthService,

projects/example-app/src/app/books/books-routing.module.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,22 @@ import {
99
import { BookExistsGuard } from '@example-app/books/guards';
1010

1111
export const routes: Routes = [
12-
{ path: 'find', component: FindBookPageComponent },
12+
{
13+
path: 'find',
14+
component: FindBookPageComponent,
15+
data: { title: 'Find book' },
16+
},
1317
{
1418
path: ':id',
1519
component: ViewBookPageComponent,
1620
canActivate: [BookExistsGuard],
21+
data: { title: 'Book details' },
22+
},
23+
{
24+
path: '',
25+
component: CollectionPageComponent,
26+
data: { title: 'Collection' },
1727
},
18-
{ path: '', component: CollectionPageComponent },
1928
];
2029

2130
@NgModule({
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import * as LayoutActions from './layout.actions';
2+
import * as UserActions from './user.actions';
23

3-
export { LayoutActions };
4+
export { LayoutActions, UserActions };
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { createAction } from '@ngrx/store';
2+
3+
export const idleTimeout = createAction('[User] Idle Timeout');
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './user.effects';
2+
export * from './router.effects';
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
2+
import { Title } from '@angular/platform-browser';
3+
import { TestBed } from '@angular/core/testing';
4+
5+
import { of } from 'rxjs';
6+
7+
import { RouterEffects } from '@example-app/core/effects';
8+
9+
describe('RouterEffects', () => {
10+
let effects: RouterEffects;
11+
let titleService: Title;
12+
13+
beforeEach(() => {
14+
TestBed.configureTestingModule({
15+
providers: [
16+
RouterEffects,
17+
{
18+
provide: Router,
19+
useValue: { events: of(new NavigationEnd(1, '', '')) },
20+
},
21+
{
22+
provide: ActivatedRoute,
23+
useValue: { data: of({ title: 'Search' }) },
24+
},
25+
{ provide: Title, useValue: { setTitle: jest.fn() } },
26+
],
27+
});
28+
29+
effects = TestBed.get(RouterEffects);
30+
titleService = TestBed.get(Title);
31+
});
32+
33+
describe('updateTitle$', () => {
34+
it('should update the title on router navigation', () => {
35+
effects.updateTitle$.subscribe();
36+
expect(titleService.setTitle).toHaveBeenCalledWith(
37+
'Book Collection - Search'
38+
);
39+
});
40+
});
41+
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Injectable } from '@angular/core';
2+
import { Router, NavigationEnd, ActivatedRoute } from '@angular/router';
3+
import { Title } from '@angular/platform-browser';
4+
5+
import { tap, filter, map, mergeMap } from 'rxjs/operators';
6+
7+
import { createEffect } from '@ngrx/effects';
8+
9+
@Injectable()
10+
export class RouterEffects {
11+
updateTitle$ = createEffect(
12+
() =>
13+
this.router.events.pipe(
14+
filter(event => event instanceof NavigationEnd),
15+
map(() => {
16+
let route = this.activatedRoute;
17+
while (route.firstChild) route = route.firstChild;
18+
return route;
19+
}),
20+
mergeMap(route => route.data),
21+
map(data => `Book Collection - ${data['title']}`),
22+
tap(title => this.titleService.setTitle(title))
23+
),
24+
{
25+
dispatch: false,
26+
}
27+
);
28+
29+
constructor(
30+
private router: Router,
31+
private titleService: Title,
32+
private activatedRoute: ActivatedRoute
33+
) {}
34+
}

0 commit comments

Comments
 (0)