Skip to content

Commit 792b63e

Browse files
committed
add side effects authj
1 parent 4b9bc90 commit 792b63e

14 files changed

+172
-66
lines changed

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
"@angular/platform-browser": "^13.0.3",
2626
"@angular/platform-browser-dynamic": "^13.0.3",
2727
"@angular/router": "^13.0.3",
28-
"@ngrx/data": "^8.0.1",
29-
"@ngrx/effects": "^8.0.1",
30-
"@ngrx/entity": "^8.0.1",
31-
"@ngrx/router-store": "^8.0.1",
28+
"@ngrx/data": "13.0.2",
29+
"@ngrx/effects": "13.0.2",
30+
"@ngrx/entity": "13.0.2",
31+
"@ngrx/router-store": "13.0.2",
3232
"@ngrx/store": "13.0.2",
3333
"@ngrx/store-devtools": "13.0.2",
3434
"body-parser": "^1.18.2",

src/app/app.component.html

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,32 @@
11
<mat-sidenav-container fullscreen>
2-
3-
<mat-sidenav #start>
4-
<mat-nav-list (click)="start.close()">
5-
6-
<a mat-list-item routerLink="/courses">
7-
<mat-icon>library_books</mat-icon>
8-
<span>Courses</span>
9-
</a>
10-
11-
<a mat-list-item routerLink="/login">
12-
<mat-icon>account_circle</mat-icon>
13-
<span>Login</span>
14-
</a>
15-
16-
<a mat-list-item (click)="logout()">
17-
<mat-icon>exit_to_app</mat-icon>
18-
<span>Logout</span>
19-
</a>
20-
21-
</mat-nav-list>
22-
23-
</mat-sidenav>
24-
25-
<mat-toolbar color="primary">
26-
<button class="menu-button" mat-icon-button (click)="start.open('mouse')">
27-
<mat-icon>menu</mat-icon>
28-
</button>
29-
</mat-toolbar>
2+
<mat-sidenav #start>
3+
<mat-nav-list (click)="start.close()">
4+
<a mat-list-item routerLink="/courses">
5+
<mat-icon>library_books</mat-icon>
6+
<span>Courses</span>
7+
</a>
8+
9+
<a mat-list-item *ngIf="isLoggedOut$ | async" routerLink="/login">
10+
<mat-icon>account_circle</mat-icon>
11+
<span>Login</span>
12+
</a>
13+
14+
<a mat-list-item *ngIf="isLoggedIn$ | async" (click)="logout()">
15+
<mat-icon>exit_to_app</mat-icon>
16+
<span>Logout</span>
17+
</a>
18+
</mat-nav-list>
19+
</mat-sidenav>
20+
21+
<mat-toolbar color="primary">
22+
<button class="menu-button" mat-icon-button (click)="start.open('mouse')">
23+
<mat-icon>menu</mat-icon>
24+
</button>
25+
</mat-toolbar>
3026

3127
<div class="spinner-container" *ngIf="loading">
32-
3328
<mat-spinner></mat-spinner>
34-
3529
</div>
3630

37-
<router-outlet></router-outlet>
38-
31+
<router-outlet></router-outlet>
3932
</mat-sidenav-container>

src/app/app.component.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { select, Store } from "@ngrx/store";
33
import { Observable } from "rxjs";
44
import { map } from 'rxjs/operators';
55
import { NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router';
6+
import { State } from './auth/store/reducers';
7+
import { AuthActions } from './auth/store/actions/auth.action.types';
8+
import { isLoggedIn, isLoggedOut } from './auth/auth.selector';
9+
import { User } from './auth/model/user.model';
610

711
@Component({
812
selector: 'app-root',
@@ -13,7 +17,12 @@ export class AppComponent implements OnInit {
1317

1418
loading = true;
1519

16-
constructor(private router: Router) {
20+
isLoggedIn$ = this.store.pipe(select(isLoggedIn));
21+
isLoggedOut$ = this.store.pipe(select(isLoggedOut));
22+
23+
constructor(
24+
private store: Store<any>,
25+
private router: Router) {
1726

1827
}
1928

@@ -38,10 +47,14 @@ export class AppComponent implements OnInit {
3847
}
3948
});
4049

50+
const user = JSON.parse(localStorage.getItem('user')) as User;
51+
if (user) {
52+
this.store.dispatch(AuthActions.login({ user }))
53+
}
4154
}
4255

4356
logout() {
44-
57+
this.store.dispatch(AuthActions.logout());
4558
}
4659

4760
}

src/app/app.module.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@ import { RouterState, StoreRouterConnectingModule } from '@ngrx/router-store';
2121
import { EffectsModule } from '@ngrx/effects';
2222
import { EntityDataModule } from '@ngrx/data';
2323
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
24+
import { AuthGuard } from './guards/auth.guard';
25+
import { entityConfig } from './entity-metadata';
2426

2527

2628
const routes: Routes = [
2729
{
2830
path: 'courses',
31+
canActivate: [AuthGuard],
2932
loadChildren: () => import('./courses/courses.module').then(m => m.CoursesModule)
3033
},
3134
{
@@ -53,7 +56,10 @@ const routes: Routes = [
5356
MatToolbarModule,
5457
AuthModule.forRoot(),
5558
StoreModule.forRoot({}, {}),
56-
StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: environment.production })
59+
StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: environment.production }),
60+
EffectsModule.forRoot([]),
61+
EntityDataModule.forRoot(entityConfig),
62+
StoreRouterConnectingModule.forRoot()
5763
],
5864
bootstrap: [AppComponent]
5965
})

src/app/auth/auth.module.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import { MatButtonModule } from "@angular/material/button";
99
import { StoreModule } from '@ngrx/store';
1010
import { AuthService } from "./auth.service";
1111
import { EffectsModule } from '@ngrx/effects';
12-
import * as fromAuth from './reducers';
12+
import * as fromAuth from './store/reducers';
13+
import { AuthEffects } from './store/auth.effects';
1314

1415
@NgModule({
1516
imports: [
@@ -20,8 +21,9 @@ import * as fromAuth from './reducers';
2021
MatButtonModule,
2122
RouterModule.forChild([{ path: '', component: LoginComponent }]),
2223
StoreModule.forFeature(fromAuth.authFeatureKey, fromAuth.authReducer),
23-
24+
EffectsModule.forFeature([AuthEffects]),
2425
],
26+
providers: [AuthEffects],
2527
declarations: [LoginComponent],
2628
exports: [LoginComponent]
2729
})

src/app/auth/auth.selector.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { createFeatureSelector, createSelector } from "@ngrx/store";
2+
import { authFeatureKey, State } from "./store/reducers";
3+
4+
export const selectAuth = createFeatureSelector<State>(
5+
authFeatureKey
6+
)
7+
8+
export const isLoggedIn = createSelector(
9+
selectAuth,
10+
auth => !!auth.user
11+
);
12+
13+
export const isLoggedOut = createSelector(
14+
isLoggedIn,
15+
loggedIn => !loggedIn
16+
);

src/app/auth/login/login.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { AuthService } from "../auth.service";
77
import { tap } from "rxjs/operators";
88
import { noop } from "rxjs";
99
import { Router } from "@angular/router";
10-
import * as fromAuth from '../reducers';
11-
import { AuthActions } from '../reducers/auth.action.types';
10+
import * as fromAuth from '../store/reducers';
11+
import { AuthActions } from '../store/actions/auth.action.types';
1212

1313
@Component({
1414
selector: 'login',

src/app/auth/reducers/auth.actions.ts renamed to src/app/auth/store/actions/auth.actions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createAction, props } from "@ngrx/store";
2-
import { User } from "../model/user.model";
2+
import { User } from "../../model/user.model";
33

44
export const login = createAction(
55
'[Login Page] User Login',

src/app/auth/store/auth.effects.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Injectable } from "@angular/core";
2+
import { Router } from "@angular/router";
3+
import { Actions, createEffect, ofType } from "@ngrx/effects";
4+
import { INIT, Store } from "@ngrx/store";
5+
import { INIT_ACTION } from "@ngrx/store-devtools/src/reducer";
6+
import { map, switchMap, tap } from "rxjs/operators";
7+
import { User } from "../model/user.model";
8+
import { AuthActions } from "./actions/auth.action.types";
9+
import { State } from "./reducers";
10+
11+
@Injectable()
12+
export class AuthEffects {
13+
14+
login$ = createEffect(() =>
15+
this.actions$.pipe(
16+
ofType(AuthActions.login),
17+
tap(auth => localStorage.setItem('user', JSON.stringify(auth.user)))
18+
), { dispatch: false });
19+
20+
logout$ = createEffect(() =>
21+
this.actions$.pipe(
22+
ofType(AuthActions.logout),
23+
tap(_ => localStorage.removeItem('user')),
24+
tap(_ => this.router.navigate(['/login']))
25+
), { dispatch: false });
26+
27+
constructor(
28+
private actions$: Actions,
29+
private router: Router,
30+
private store: Store<State>) { }
31+
}

src/app/auth/reducers/index.ts renamed to src/app/auth/store/reducers/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import {
77
MetaReducer,
88
on,
99
} from '@ngrx/store';
10-
import { environment } from '../../../environments/environment';
11-
import { User } from '../model/user.model';
12-
import { AuthActions } from './auth.action.types';
10+
import { environment } from '../../../../environments/environment';
11+
import { User } from '../../model/user.model';
12+
import { AuthActions } from '../actions/auth.action.types';
1313

1414
export const authFeatureKey = 'auth';
1515

src/app/entity-metadata.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { EntityMetadataMap, EntityDataModuleConfig } from '@ngrx/data';
2+
3+
const entityMetadata: EntityMetadataMap = {};
4+
5+
const pluralNames = { };
6+
7+
export const entityConfig: EntityDataModuleConfig = {
8+
entityMetadata,
9+
pluralNames
10+
};

src/app/guards/auth.guard.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Injectable } from '@angular/core';
2+
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
3+
import { select, Store } from '@ngrx/store';
4+
import { Observable } from 'rxjs';
5+
import { first, map } from 'rxjs/operators';
6+
import { isLoggedIn } from '../auth/auth.selector';
7+
import { State } from '../auth/store/reducers';
8+
9+
@Injectable({
10+
providedIn: 'root'
11+
})
12+
export class AuthGuard implements CanActivate, CanActivateChild {
13+
constructor(
14+
private router: Router,
15+
private store: Store<State>) { }
16+
17+
canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> {
18+
return this.canActivate(childRoute, state);
19+
}
20+
21+
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> {
22+
return this.store.pipe(
23+
first(),
24+
select(isLoggedIn),
25+
map(isLoggedIn => isLoggedIn ? true : this.router.parseUrl('/login')));
26+
}
27+
}

yarn.lock

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,25 +1254,33 @@
12541254
"@jridgewell/resolve-uri" "^3.0.3"
12551255
"@jridgewell/sourcemap-codec" "^1.4.10"
12561256

1257-
"@ngrx/data@^8.0.1":
1258-
version "8.6.1"
1259-
resolved "https://registry.yarnpkg.com/@ngrx/data/-/data-8.6.1.tgz#a1f21ee726d88f4ad4eaba1a1e423fd16fc07c19"
1260-
integrity sha512-+fc7vSN4flsiNWzMaxOVx54wolgsrLWib2oLEDRKAyyIGtW/dVb/lQmfIUUS4bfBT8gxE/IBLXCTtbwR6IGz/Q==
1261-
1262-
"@ngrx/effects@^8.0.1":
1263-
version "8.6.1"
1264-
resolved "https://registry.yarnpkg.com/@ngrx/effects/-/effects-8.6.1.tgz#6038a5e9cc3db27fdea4cfbcc6e2ae63721e8a26"
1265-
integrity sha512-nvokfJ+LXez3DuAVSWW4w/eMrjPoSpzyHSToIVbDTVIF4JZ3g8fKdi9aHYAeXy2MDR1BaV7zMTmM9w6J0TDZtw==
1266-
1267-
"@ngrx/entity@^8.0.1":
1268-
version "8.6.1"
1269-
resolved "https://registry.yarnpkg.com/@ngrx/entity/-/entity-8.6.1.tgz#8b227746c1009adcf172a205aec30b416222428a"
1270-
integrity sha512-hfwWxAthKKL9d+7IjUpQ0pAPIdjuGlLQD6/wHNb+w9MmqlCwY15ggdHc5hgr2Zd/+XJO1qRqOekKSQQRZC8bXQ==
1271-
1272-
"@ngrx/router-store@^8.0.1":
1273-
version "8.6.1"
1274-
resolved "https://registry.yarnpkg.com/@ngrx/router-store/-/router-store-8.6.1.tgz#b631f8d6d2b7204ab3396b9d678f0134c674fe3d"
1275-
integrity sha512-WOFSpxfaePoFu6gYJbXs7vBH8YG/m27ySeN9jBmlO94CkwWN9w3XXQebMVu47EoKO+YpMlF2l2Ifakhefq0TWQ==
1257+
"@ngrx/data@13.0.2":
1258+
version "13.0.2"
1259+
resolved "https://registry.yarnpkg.com/@ngrx/data/-/data-13.0.2.tgz#f9ef155bf6fdd874ce6b82a76ce5f2f2cb09a0de"
1260+
integrity sha512-DpPxmtmG9AprXABmreXH7ilHQDGawHFJyNUOR/WS+dstamtFO0WMIr4BhNYRdDGBWqcXWNyuEj14ipjKCTfdKw==
1261+
dependencies:
1262+
tslib "^2.0.0"
1263+
1264+
"@ngrx/effects@13.0.2":
1265+
version "13.0.2"
1266+
resolved "https://registry.yarnpkg.com/@ngrx/effects/-/effects-13.0.2.tgz#785e54459efaef70ed3754a33d9f4ddd0ff9f033"
1267+
integrity sha512-7yW/KCxlRatDkdEriSnORlOYX8+1QAWEjPulNmHSPwehkzTQ3fIPfRBQy8xP8bnjwvGxnEZNwQlU4q1KVYOfhg==
1268+
dependencies:
1269+
tslib "^2.0.0"
1270+
1271+
"@ngrx/entity@13.0.2":
1272+
version "13.0.2"
1273+
resolved "https://registry.yarnpkg.com/@ngrx/entity/-/entity-13.0.2.tgz#99fceb9f160de67106364e27299e242b1608affc"
1274+
integrity sha512-sAN/YDvDNy4fmCOWU8LfPp8YI8XJcvmO89XTRSoJjo3O9BIbCSEw2gYHe1V7gWHLY5Pr9wiMybKhd4RHtzFHgQ==
1275+
dependencies:
1276+
tslib "^2.0.0"
1277+
1278+
"@ngrx/router-store@13.0.2":
1279+
version "13.0.2"
1280+
resolved "https://registry.yarnpkg.com/@ngrx/router-store/-/router-store-13.0.2.tgz#e57084cd74071760378645417bba94805c8b4514"
1281+
integrity sha512-XrzHjrD2hhnXdGeIpQm/msN77hoAL/QD3ZYGFJs3yT5d3x/T3L1JFlra7wC0OlKJkOs6zAh5Kz9cJ94YO/TEtQ==
1282+
dependencies:
1283+
tslib "^2.0.0"
12761284

12771285
"@ngrx/schematics@^13.0":
12781286
version "13.0.2"

0 commit comments

Comments
 (0)