Skip to content

Commit d05b0db

Browse files
committed
initial commit
1 parent cb4764a commit d05b0db

11 files changed

+293
-30
lines changed

src/app/app.component.ts

+42-22
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,52 @@
11
import { Component } from '@angular/core';
2+
import { NavigationEnd, Router } from '@angular/router';
3+
import { filter } from 'rxjs/operators';
4+
import { AuthService } from './core/auth/auth.service';
25

36
@Component({
47
selector: 'app-root',
58
template: `
6-
<!--The content below is only a placeholder and can be replaced.-->
7-
<div style="text-align:center" class="content">
8-
<h1>
9-
Welcome to {{title}}!
10-
</h1>
11-
<span style="display: block">{{ title }} app is running!</span>
12-
<img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
13-
</div>
14-
<h2>Here are some links to help you start: </h2>
15-
<ul>
16-
<li>
17-
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
18-
</li>
19-
<li>
20-
<h2><a target="_blank" rel="noopener" href="https://angular.io/cli">CLI Documentation</a></h2>
21-
</li>
22-
<li>
23-
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
24-
</li>
25-
</ul>
9+
<button
10+
routerLink='/admin'
11+
routerLinkActive='active'
12+
*ngIf='(authService.isLogged$ | async)'
13+
>
14+
Admin (Protected)
15+
</button>
16+
17+
<button routerLink='/home' routerLinkActive='active'>HOME</button>
18+
19+
<button
20+
routerLink='/'
21+
*ngIf='!(authService.isLogged$ | async)'
22+
[routerLinkActiveOptions]='{ exact: true }'
23+
routerLinkActive='active'
24+
>
25+
Login
26+
</button>
27+
28+
<button (click)='logout()' *appIfLogged>LOGOUT</button>
29+
30+
<hr />
2631
<router-outlet></router-outlet>
2732
`,
28-
styles: []
33+
styles: [
34+
`
35+
.active {
36+
background: orange;
37+
}
38+
`
39+
]
2940
})
3041
export class AppComponent {
31-
title = 'angular-rxjs-real-world';
42+
constructor(private router: Router, public authService: AuthService) {
43+
this.router.events
44+
.pipe(filter(event => event instanceof NavigationEnd))
45+
.subscribe(res => console.log(res));
46+
}
47+
48+
logout(): void {
49+
this.authService.logout();
50+
this.router.navigateByUrl('/');
51+
}
3252
}

src/app/app.module.ts

+30-7
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,41 @@
1-
import { NgModule } from '@angular/core';
2-
import { BrowserModule } from '@angular/platform-browser';
1+
import {BrowserModule} from '@angular/platform-browser';
2+
import {NgModule} from '@angular/core';
3+
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
4+
import {RouterModule, Routes} from '@angular/router';
5+
import {FormsModule} from '@angular/forms';
36

4-
import { AppRoutingModule } from './app-routing.module';
5-
import { AppComponent } from './app.component';
7+
import {AppComponent} from './app.component';
8+
import {AdminComponent} from './features/admin/admin.component';
9+
import {LoginComponent} from './features/login/login.component';
10+
import {HomeComponent} from './features/home/home.component';
11+
import {IfloggedDirective} from './core/auth/iflogged.directive';
12+
13+
import {AuthGuard} from './core/auth/auth.guard';
14+
import {AuthInteceptor} from './core/auth/auth.interceptor';
15+
16+
const appRoutes: Routes = [
17+
{ path: 'admin', component: AdminComponent, canActivate: [AuthGuard] },
18+
{ path: 'home', component: HomeComponent },
19+
{ path: '', component: LoginComponent },
20+
];
621

722
@NgModule({
823
declarations: [
9-
AppComponent
24+
AppComponent,
25+
AdminComponent,
26+
LoginComponent,
27+
HomeComponent,
28+
IfloggedDirective
1029
],
1130
imports: [
1231
BrowserModule,
13-
AppRoutingModule
32+
FormsModule,
33+
HttpClientModule,
34+
RouterModule.forRoot(appRoutes)
35+
],
36+
providers: [
37+
{provide: HTTP_INTERCEPTORS, useClass: AuthInteceptor, multi: true}
1438
],
15-
providers: [],
1639
bootstrap: [AppComponent]
1740
})
1841
export class AppModule { }

src/app/core/auth/auth.guard.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Injectable } from '@angular/core';
2+
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
3+
import { AuthService } from './auth.service';
4+
import { Observable } from 'rxjs';
5+
import { tap } from 'rxjs/operators';
6+
7+
@Injectable({ providedIn: 'root' })
8+
export class AuthGuard implements CanActivate {
9+
10+
constructor(private authService: AuthService, private router: Router) {}
11+
12+
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
13+
return this.authService.isLogged$
14+
.pipe(
15+
tap(value => {
16+
// if not logged...
17+
if (!value) {
18+
// redirect to login
19+
console.log('access deny');
20+
this.router.navigateByUrl('/');
21+
}
22+
})
23+
);
24+
}
25+
}

src/app/core/auth/auth.interceptor.ts

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { Injectable } from '@angular/core';
2+
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
3+
import { Observable, of } from 'rxjs';
4+
import { AuthService } from './auth.service';
5+
import { catchError, first, mergeMap, withLatestFrom } from 'rxjs/operators';
6+
import { iif } from 'rxjs';
7+
import { Router } from '@angular/router';
8+
9+
@Injectable({ providedIn: 'root' })
10+
export class AuthInteceptor implements HttpInterceptor {
11+
12+
constructor(private authService: AuthService, private router: Router) {
13+
}
14+
15+
intercept(
16+
req: HttpRequest<any>,
17+
next: HttpHandler
18+
): Observable<HttpEvent<any>> {
19+
return this.authService.isLogged$
20+
.pipe(
21+
first(),
22+
withLatestFrom(this.authService.token$),
23+
mergeMap(([isLogged, tk]) =>
24+
iif(() => isLogged,
25+
next.handle(req.clone({ setHeaders: { Authorization: `Token ${tk}` } })),
26+
next.handle(req)
27+
)
28+
)
29+
)
30+
.pipe(
31+
catchError(err => {
32+
if (err instanceof HttpErrorResponse) {
33+
switch (err.status) {
34+
case 404:
35+
console.log('redirect to login');
36+
this.authService.logout();
37+
this.router.navigateByUrl('/');
38+
break;
39+
default:
40+
}
41+
}
42+
return of(err); // or throwError
43+
})
44+
);
45+
}
46+
}

src/app/core/auth/auth.service.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Injectable } from '@angular/core';
2+
import { root } from 'rxjs/internal-compatibility';
3+
import { HttpClient } from '@angular/common/http';
4+
import { BehaviorSubject, Observable } from 'rxjs';
5+
import { map } from 'rxjs/operators';
6+
7+
@Injectable({ providedIn: 'root' })
8+
export class AuthService {
9+
10+
token$: BehaviorSubject<string> = new BehaviorSubject<string>(null);
11+
12+
constructor(private http: HttpClient) {
13+
}
14+
15+
login(): void {
16+
this.http.get<any>('https://my-json-server.typicode.com/marco7403/repo/login')
17+
.subscribe(res => {
18+
this.token$.next(res.token);
19+
});
20+
}
21+
22+
logout(): void {
23+
this.token$.next(null);
24+
}
25+
26+
get isLogged$(): Observable<boolean> {
27+
return this.token$.pipe(
28+
map(value => !!value)
29+
);
30+
}
31+
}
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Directive, OnDestroy, OnInit, TemplateRef, ViewContainerRef } from '@angular/core';
2+
import { Subject } from 'rxjs';
3+
import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
4+
import { AuthService } from './auth.service';
5+
6+
@Directive({
7+
selector: '[appIfLogged]'
8+
})
9+
export class IfloggedDirective implements OnInit, OnDestroy {
10+
private destroy$ = new Subject();
11+
12+
constructor(
13+
private template: TemplateRef<any>,
14+
private view: ViewContainerRef,
15+
private authService: AuthService
16+
) {}
17+
18+
ngOnInit(): void {
19+
this.authService.isLogged$
20+
.pipe(
21+
distinctUntilChanged(),
22+
takeUntil(this.destroy$)
23+
)
24+
.subscribe(isLogged => {
25+
if (isLogged) {
26+
this.view.createEmbeddedView(this.template);
27+
} else {
28+
this.view.clear();
29+
}
30+
});
31+
}
32+
33+
ngOnDestroy(): void {
34+
this.destroy$.next();
35+
this.destroy$.complete();
36+
}
37+
}
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import {Component} from '@angular/core';
2+
import {AuthService} from '../../core/auth/auth.service';
3+
import {HttpClient} from '@angular/common/http';
4+
import { Router } from '@angular/router'
5+
6+
@Component({
7+
selector: 'app-admin',
8+
template: `
9+
{{this.tweets | json}}
10+
<hr />
11+
<button (click)="logoutHandler()">Logout</button>
12+
`,
13+
styles: []
14+
})
15+
export class AdminComponent {
16+
tweets: any[];
17+
18+
constructor(
19+
public authService: AuthService,
20+
private http: HttpClient,
21+
private router: Router
22+
) {
23+
24+
this.http.get<any>('https://my-json-server.typicode.com/marco7403/repo/tweets')
25+
.subscribe(res => this.tweets = res);
26+
}
27+
28+
logoutHandler() {
29+
this.router.navigateByUrl('/')
30+
this.authService.logout();
31+
}
32+
33+
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {Component} from '@angular/core';
2+
3+
@Component({
4+
selector: 'app-home',
5+
template: `
6+
<h1>Home</h1>
7+
`,
8+
})
9+
export class HomeComponent {
10+
11+
}
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Component } from '@angular/core';
2+
import { AuthService } from '../../core/auth/auth.service';
3+
import { Router } from '@angular/router';
4+
import { filter } from 'rxjs/operators';
5+
6+
@Component({
7+
selector: 'app-login',
8+
template: `
9+
<form>
10+
<input type="text" value="anyvalue">
11+
<input type="password" value="123">
12+
<button (click)="login()">Sign In</button>
13+
</form>
14+
`,
15+
styles: []
16+
})
17+
export class LoginComponent {
18+
19+
constructor(
20+
private authService: AuthService,
21+
private route: Router
22+
) {
23+
this.authService.isLogged$
24+
.pipe(filter(state => !!state))
25+
.subscribe(() => {
26+
console.log('go to admin');
27+
this.route.navigateByUrl('admin');
28+
});
29+
}
30+
31+
login() {
32+
this.authService.login();
33+
}
34+
}

src/styles.css

+3-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
/* You can add global styles to this file, and also import other style files */
1+
p {
2+
font-family: Lato;
3+
}

tslint.json

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"codelyzer"
55
],
66
"rules": {
7+
"no-trailing-whitespace": false,
78
"align": {
89
"options": [
910
"parameters",

0 commit comments

Comments
 (0)