Last active
April 27, 2020 12:20
-
-
Save kazuma1989/8dc8a5e148fcf1737540f3d531feb151 to your computer and use it in GitHub Desktop.
Angular Guard の動作確認と、ローディング中表示の実装
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
import { NgModule } from '@angular/core'; | |
import { Routes, RouterModule } from '@angular/router'; | |
import { FooComponent } from 'app/foo/foo.component'; | |
import { BarComponent } from 'app/bar/bar.component'; | |
import { AuthGuard } from 'app/guard/auth.guard'; | |
import { PubComponent } from 'app/pub/pub.component'; | |
import { NotFoundComponent } from 'app/not-found/not-found.component'; | |
const routes: Routes = [ | |
{ | |
path: 'pub', | |
component: PubComponent | |
}, | |
{ | |
path: '', | |
// canActivate に指定すると | |
// /pub <-> /foo/1 or /bar 間の遷移のときに canActivate() が呼ばれる | |
// /foo/1 <-> /bar 間の遷移のときは呼ばれない | |
canActivate: [AuthGuard], | |
// canActivateChild に指定すると、 | |
// /pub <-> /foo/1 or /bar 間の遷移のとき | |
// /foo/1 <-> /bar 間の遷移のとき | |
// どちらも canActivateChild() が呼ばれる | |
canActivateChild: [AuthGuard], | |
children: [ | |
{ | |
path: 'foo/:id', | |
// resolve に指定すると、遷移前に resolve() メソッドが呼ばれ、 | |
// その戻り値の Observable が解決したあとに遷移する。 | |
// :id で指定したアイテムを HTTP API から取得してから遷移、ということが可能。 | |
// 取得したデータは { item: someItem } のようなハッシュとして得られる(AuthGuard を item というキーに割り当てているので)。 | |
// AuthGuard にやらせるのは間違っているが、Service を増やすのが面倒なのでこうしている | |
resolve: { | |
item: AuthGuard | |
}, | |
component: FooComponent | |
}, | |
{ | |
path: 'bar', | |
component: BarComponent | |
}, | |
], | |
}, | |
{ | |
path: '**', | |
component: NotFoundComponent | |
} | |
]; | |
@NgModule({ | |
imports: [RouterModule.forRoot(routes)], | |
exports: [RouterModule] | |
}) | |
export class AppRoutingModule { } |
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
<ul> | |
<li> | |
<a routerLink="/pub">/pub</a> | |
</li> | |
<li> | |
<a routerLink="/foo/a">/foo/a</a> | |
</li> | |
<li> | |
<a routerLink="/foo/404">/foo/404</a> | |
</li> | |
<li> | |
<a routerLink="/bar">/bar</a> | |
</li> | |
</ul> | |
<router-outlet></router-outlet> | |
<app-loading></app-loading> |
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
import { BrowserModule } from '@angular/platform-browser'; | |
import { NgModule } from '@angular/core'; | |
import { AppRoutingModule } from './app-routing.module'; | |
import { AppComponent } from './app.component'; | |
import { AuthGuard } from './guard/auth.guard'; | |
import { FooComponent } from './foo/foo.component'; | |
import { BarComponent } from './bar/bar.component'; | |
import { PubComponent } from './pub/pub.component'; | |
import { LoadingService } from './service/loading.service'; | |
import { LoadingComponent } from './loading/loading.component'; | |
import { NotFoundComponent } from './not-found/not-found.component'; | |
@NgModule({ | |
declarations: [ | |
AppComponent, | |
FooComponent, | |
BarComponent, | |
PubComponent, | |
LoadingComponent, | |
NotFoundComponent | |
], | |
imports: [ | |
BrowserModule, | |
AppRoutingModule | |
], | |
providers: [AuthGuard, LoadingService], | |
bootstrap: [AppComponent] | |
}) | |
export class AppModule { } |
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
import { Injectable } from '@angular/core'; | |
import { Router, CanActivate, CanActivateChild, Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; | |
import { Observable } from 'rxjs/Observable'; | |
import 'rxjs/add/observable/of'; | |
import 'rxjs/add/observable/throw'; | |
import 'rxjs/add/operator/delay'; | |
import 'rxjs/add/operator/do'; | |
import { LoadingService } from 'app/service/loading.service'; | |
@Injectable() | |
export class AuthGuard implements CanActivate, CanActivateChild, Resolve<any> { | |
constructor( | |
// LoadingService は独自のサービスで、ロード中の「ぐるぐる」を制御するサービス | |
private loading: LoadingService, | |
private router: Router | |
) { } | |
canActivate( | |
next: ActivatedRouteSnapshot, | |
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean { | |
console.log('canActivate'); | |
return true; | |
} | |
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean { | |
console.log('canActivateChild'); | |
return true; | |
} | |
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> { | |
const id = route.paramMap.get('id'); | |
console.log(state.url, id); | |
this.loading.start(); | |
// id に該当するアイテムを HTTP API から取得する、というのを模倣している | |
let result; | |
if (id === 'a') { | |
result = Observable.of({ | |
id, | |
name: 'A' | |
}); | |
} else { | |
result = Observable.throw(`${id} not found`); | |
} | |
// 遷移を呼び出してから 1000 ms 後に Observable が解決し、遷移が始まる | |
return result.delay(1000).do( | |
// onnext のときはやることがないのでスルー | |
null, | |
// onerror のときは Not found ページへ遷移 | |
// URL を書き換えないほうがいいので(どのページへ遷移しようとしてエラーになったかがわからなくなってしまうため)、 | |
// skipLocationChange オプションを有効にしている。 | |
() => { | |
this.router.navigate(['404'], { | |
skipLocationChange: true | |
}); | |
this.loading.end(); | |
}, | |
// oncomplete のとき、LoadingService を止める | |
() => this.loading.end(), | |
); | |
} | |
} |
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
import { Component, OnInit } from '@angular/core'; | |
import { LoadingService } from 'app/service/loading.service'; | |
@Component({ | |
selector: 'app-loading', | |
template: ` | |
<div [hidden]="hidden"> | |
<div class="wrapper"> | |
<div class="loader"></div> | |
<span class="loader-text">Loading...</span> | |
</div> | |
</div> | |
`, | |
style: ` | |
.wrapper { | |
display: flex; | |
flex-direction: column; | |
justify-content: center; | |
align-items: center; | |
height: 100vh; | |
color: white; | |
position: fixed; | |
top: 0; | |
width: 100%; | |
background-color: rgba(0, 0, 0, 0.5); | |
} | |
.loader { | |
border: 16px solid #f3f3f3; | |
border-top: 16px solid #4055ae; | |
border-radius: 50%; | |
width: 120px; | |
height: 120px; | |
animation: spin 2s linear infinite; | |
} | |
@keyframes spin { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.loader-text { | |
display: block; | |
margin-top: 1em; | |
} | |
` | |
}) | |
export class LoadingComponent implements OnInit { | |
hidden: boolean; | |
constructor( | |
private loading: LoadingService | |
) { } | |
ngOnInit() { | |
this.hidden = true; | |
// LoadingService#end() が呼び出されると hidden 状態に戻る | |
this.loading.subject.subscribe(value => { | |
this.hidden = value === 'end'; | |
}); | |
} | |
} |
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
import { Injectable } from '@angular/core'; | |
import { Subject } from 'rxjs/Subject'; | |
@Injectable() | |
export class LoadingService { | |
subject: Subject<any>; | |
constructor() { | |
this.subject = new Subject<any>(); | |
} | |
start() { | |
console.log('start'); | |
this.subject.next('start'); | |
} | |
end() { | |
console.log('end'); | |
this.subject.next('end'); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
メインのファイルは二つ
それにより理解できたこと
おまけ