今天開始 今天完成
今天的目標:將會員有關的元件及服務從使用者服務轉為向 ngrx/store 做 dispatch 跟 select
會員登入元件
第一步:修改 src/app/user/login/login.component.ts
// … 省略
import { Store } from ‘@ngrx/store’;
import * as fromStore from ‘../../store’;
//… 省略
export class LoginComponent implements OnInit {
public form: FormGroup;
constructor(
private fb: FormBuilder,
private store: Store<fromStore.State>,
private router: Router,
private snackbar: MatSnackBar
) { }
login() {
this.store.dispatch(new fromStore.LoginAction(this.form.value));
this.store.select(fromStore.getIsLogin)
.subscribe(res => {
if (res) {
this.snackbar.open(‘登入成功’, ‘OK’, { duration: 3000 });
this.router.navigate([‘/member’]);
} else {
this.snackbar.open(‘請檢查使用者名稱及密碼’, ‘OK’, { duration: 3000 });
}
})
}
}
將 UserService 刪除換成 Store,並且 import 我們之前寫的 store 成為 fromStore
constructor 導入 Store
將原本 userService 的部分換成 store.disptch(new fromStore.LoginAction(),接著用 store.select(fromStore.getIsLogin) 來選擇狀態樹中 login 的狀態
檢查一下瀏覽器,一且順利的話,畫面截圖如下
修改路由防護 (Auth.Guard)
修改 src/app/guards/auth.guards.ts
//…省略
import { Store } from ‘@ngrx/store’;
import * as fromStore from ‘../store’;
import { tap, take } from ‘rxjs/operators’;
import { of } from ‘rxjs/observable/of’;
@Injectable()
export class AuthGuard implements CanActivate, CanLoad {
loginStatus$: Observable<boolean>
constructor(
private store: Store<fromStore.State>,
private router: Router
) {
this.loginStatus$ = store.select(fromStore.getIsLogin);
}
//… 省略
一樣將使用者服務刪除,導入 Store,在 constructor 將 loginStatus$ 變為 store.select(fromStore.getIsLogin) 就完成
修改瀏覽列
現在登入後會轉到會員畫面,但是瀏覽列還沒變,我們來修改一下
src/app/navbar/navbar.component.ts
//… 省略
import { Store } from ‘@ngrx/store’;
import { User } from ‘../models’;
//… 省略
export class NavbarComponent implements OnInit {
login$: Observable<boolean>;
user$: Observable<string>;
constructor(
private store: Store<fromStore.State>,
private router: Router
) { }
ngOnInit() {
this.login$ = this.store.select(fromStore.getIsLogin);
this.user$ = this.store.select(fromStore.getCurrentUser);
}
logout() {
this.store.dispatch(new fromStore.LogoutAction());
this.router.navigate([‘/’])
}
}
將使用者服務換成 Store
原來的 login$ 跟 user$ 換成用 store.select()
logout 時用 store.dispatch(new fromStore.LogoutAction()) 來改變狀態
接下來,修改開始服務
修改開始服務
src/apprvices/startup.service.ts
//… 省略
import { UtilsService } from ‘.’;
import { Store } from ‘@ngrx/store’;
import * as fromStore from ‘../store’;
@Injectable()
export class StartupService {
constructor(
private injector: Injector,
private utils: UtilsService,
private store: Store<fromStore.State>
) { }
load(): Promise<any> {
return new Promise((resolve, reject) => {
if (!this.utils.isTokenExpired()) {
this.store.dispatch(new fromStore.GetUserAction());
return this.store.select(fromStore.getIsLogin)
.pipe(
filter(status => status)
)
.subscribe(res => {
if (res) {
setInterval(() => {
this.checkStatus();
}, 1000 * 60 * 5) // check current status every 5 min
}
resolve(res);
}, err => {
console.log(err);
reject(err);
});
} else {
resolve(‘no token or token expired’);
}
});
}
checkStatus() {
if (this.utils.isTokenExpired()) { // if token expired or not exist
this.store.dispatch(new fromStore.LogoutAction());
const router = this.injector.get(Router);
router.navigate([‘/’]);
}
}
}
刪除使用者服務換成 Store
load() 加入判斷 token 有沒有過期
使用 store.dispatch(new fromStore.GetUserAction()) 來從後端取得使用者資訊,這裡是非同步,我們用 filter 來等後端資訊完成,因為 token 未過期,所以 status 一定要是真
每五分鐘檢查,checkStatus() 當 token 過期時,使用 store.dispatch(new fromStore.LogoutAction()) 來改變狀態
最後修改使用者服務
修改使用者服務
src/app/userrvice/user.service.ts
import { Injectable } from ‘@angular/core’;
import { HttpClient, HttpErrorResponse } from ‘@angular/commontp’;
import { AppConfig } from ‘../..‘;
import { User, Response } from ‘../../models’;
import { Observable } from ‘rxjs/Observable’;
import ‘rxjs/add/operator/map’;
import { of } from ‘rxjs/observable/of’;
import { UtilsService } from ‘../..rvices’;
@Injectable()
export class UserService {
constructor(
private http: HttpClient,
private appConfig: AppConfig,
private utils: UtilsService
) { }
loginServer(loginData): Observable<Response> {
let username = loginData.username.trim();
let password = loginData.password.trim();
return this.http.post<Response>(this.appConfig.apiUrl + ‘/users/authenticate’, { username: username, password: password });
}
// get user from server
getUserFromServer(): Observable<User> {
if (!this.utils.isTokenExpired()) {
const token = this.utils.getToken();
return this.http.post(this.appConfig.apiUrl + ‘/users/currentUser’, { ‘token’: token })
.map((res: Response) => {
if (res.success) {
return { username: res.payload };
} else {
return null;
}
})
} else {
return of(null);
}
}
}
上面是完整的使用者服務,我們將所有有關狀態的部分全部刪除,只剩下跟後端連結的部分 loginServer() 跟 getUserFromServer()
修改一下 getUserFromServer(),這裡有一個 Bug, 應該要回 { username: res.payload } 而不是 res.payload 因為後端的 res.payload 只是一個 string
我們已經完成會員的部分,也就是所有跟會員有關的元件,現在只對 Store,不再經由使用者服務。
接下來在做跟報告有關的元件之前,也們先來看一下路由的部分,路由 (Router) 也可以當成是狀態的一部份,因為在報告的模組中,我們會用到 /member/report/3 這樣的路徑,而之前我們用報告服務來抓這個單獨的報告,現在我們想從 Store 的狀態樹中,結合報告狀態跟路由狀態來取得這份單獨的報告,所以我們先來做路由 (Router) 的 ngrx/store
转载请注明:XAMPP中文组官网 » ngrx/store 之會員篇