![](/img/trans.png)
[英]Angular 2 - Routing - CanActivate work with Observable
[英]Angular 4 - canActivate observable not invoked
我想實現canActivate
在角2/4使用RxJS觀測。 我已經閱讀了另一個關於此的問題 。 使用以下代碼,我的canActivate
方法僅在應用程序啟動時工作一次,但是當isLoggedIn
observable觸發新值時,永遠不會再打印hello
。
canActivate(): Observable<boolean> {
return this.authService.isLoggedIn().map(isLoggedIn => {
console.log('hello');
if (!isLoggedIn) {
this.router.navigate(['/login']);
}
return isLoggedIn;
}).first();
}
或者這個版本不能正常工作:
canActivate(): Observable<boolean> {
return this.authService.isLoggedIn().map(isLoggedIn => {
console.log('hello');
if (isLoggedIn) {
this.router.navigate(['/']);
}
return !isLoggedIn;
});
}
但是,它適用於此代碼:
canActivate(): Observable<boolean> {
return Observable.create(obs => {
this.authService.isLoggedIn().map(isLoggedIn => {
console.log('hello');
if (isLoggedIn) {
this.router.navigate(['/']);
}
return !isLoggedIn;
}).subscribe(isLoggedIn => obs.next(isLoggedIn));
});
}
我在第一段代碼中做錯了什么?
編輯:這是isLoggedIn
實現
@LocalStorage(AuthService.JWT_TOKEN_KEY)
private readonly token: string;
private tokenStream: Subject<string>;
public isLoggedIn(): Observable<boolean> {
if (!this.tokenStream) {
this.tokenStream = new BehaviorSubject(this.token);
this.storage.observe(AuthService.JWT_TOKEN_KEY)
.subscribe(token => this.tokenStream.next(token));
}
return this.tokenStream.map(token => {
return token != null
});
}
使用ngx-webstorage
。 和RxJS BehaviorSubject
。
這是我從AngularJs的承諾轉換為Angular的Observable模式時遇到的困難之一。 你看到承諾是拉動通知,而觀察者是推送通知。 因此,您必須重新考慮您的AuthService,以便它使用推送模式。 即使在我編寫工作Observables時,我一直在考慮拉動。 在拉動方面,我無法停止思考。
使用承諾模式更容易。 創建AuthService時,它將創建一個解析為“未登錄”的promise,或者它將創建一個“還原已記錄狀態”的異步保證。 然后,您可以使用名為isLoggedIn()
的方法來返回該promise。 這使您可以輕松處理顯示用戶數據和收到用戶數據之間的延遲。
現在,我們切換到Observables, 動詞 “is”需要更改為“when” 。 進行這一小改動有助於您重新思考事情的發展方向。 因此,我們將“isLoggedIn”重命名為“whenLoggedIn()”,這將是一個在用戶進行身份驗證時發出數據的Observable。
class AuthService {
private logIns: Subject = new Subject<UserData>();
public setUser(user: UserData) {
this.logIns.next(user);
}
public whenLoggedIn(): Observable<UserData> {
return this.logIns;
}
}
// example
AuthService.whenLoggedIn().subscribe(console.log);
AuthService.setUser(new UserData());
當用戶傳遞給setUser
它會發出以訂閱新用戶的身份驗證。
以上介紹了需要修復的幾個問題。
whenLoggedIn
將永遠收聽新用戶。 拉流永遠不會完成。 setUser
會丟失。 我們可以通過從Subject
切換到BehaviorSubject
來解決一些問題。
class AuthService {
private logIns: Subject = new BehaviorSubject<UserData>(null);
public setUser(user: UserData) {
this.logIns.next(user);
}
public whenLoggedIn(): Observable<UserData> {
return this.logIns;
}
}
// example
AuthService.whenLoggedIn().first().subscribe(console.log);
AuthService.setUser(new UserData());
這更接近我們想要的。
BehaviorSubject
將始終為每個新訂閱發出最后一個值。 whenLoggedIn().first()
被添加到subscribe並在收到第一個值后自動取消訂閱 。 如果我們沒有使用BehaviorSubject
那么會阻塞,直到有人調用setUser
,這可能永遠不會發生。 BehaviorSubject
不適用於AuthService,我將在此處演示此示例代碼。
class AuthService {
private logIns: Subject = new BehaviorSubject<UserData>(null);
public constructor(userSessionToken:string, tokenService: TokenService) {
if(userSessionToken) {
tokenService.create(userSessionToken).subscribe((user:UserData) => {
this.logIns.next(user);
});
}
}
public setUser(user: UserData) {
this.logIns.next(user);
}
public whenLoggedIn(): Observable<UserData> {
return this.logIns;
}
}
以下是問題在代碼中的顯示方式。
// example
let auth = new AuthService("my_token", tokenService);
auth.whenLoggedIn().first().subscribe(console.log);
上面創建了一個帶有令牌的新AuthService來恢復用戶會話,但是當它運行時,控制台只打印“null”。
發生這種情況是因為使用初始值null
創建了BehaviorSubject
,並且在HTTP調用完成后將恢復用戶會話的操作。 在會話恢復之前,AuthService將繼續發出null
,但是當您想要使用路由激活器時,這是一個問題。
我們想要記住當前用戶,但在我們知道是否有用戶之前不會發出任何內容。 ReplaySubject
是這個問題的答案。
這是你如何使用它。
class AuthService {
private logIns: Subject<UserData> = new ReplaySubject(1);
public constructor(userSessionToken:string, tokenService: TokenService) {
if(userSessionToken) {
tokenService.create(userSessionToken).subscribe((user:UserData) => {
this.logIns.next(user);
}, ()=> {
this.logIns.next(null);
console.error('could not restore session');
});
} else {
this.logIns.next(null);
}
}
public setUser(user: UserData) {
this.logIns.next(user);
}
public whenLoggedIn(): Observable<UserData> {
return this.logIns;
}
}
// example
let auth = new AuthService("my_token", tokenService);
auth.whenLoggedIn().first().subscribe(console.log);
上述內容不會等到 whenLoggedIn
發出第一個值。 它將獲得第first
值並取消訂閱。
ReplaySubject
可以正常工作,因為它可以記住1
項目或者什么都不發出。 這不是重要的部分。 當我們在canActivate
使用AuthService時,我們希望等到用戶狀態已知 。
現在,這使得編寫重定向到登錄屏幕或允許路由更改的用戶警衛變得更加容易。
class UserGuard implements CanActivate {
public constructor(private auth: AuthService, private router: Router) {
}
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.auth.whenLoggedIn()
.first()
.do((user:UserData) => {
if(user === null) {
this.router.navigate('/login');
}
})
.map((user:UserData) => !!user);
}
如果存在用戶會話,則將產生Observable為true或false。 它還會阻止路由器更改,直到該狀態已知(即我們是否從服務器獲取數據?)。
如果沒有用戶數據,它還會將路由器重定向到登錄屏幕。
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.service.callServiceMethod())
.map(result => {
return true;
}).catch(err => {
return Observable.of(false);
});
return Observable.of(true);
}
這就是我的成就
import { Injectable, Inject, Optional } from "@angular/core";
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router";
import { Observable } from "rxjs/Rx";
import { AuthService, MyLogin, FacebookLogin, GoogleLogin } from "./auth.service";
@Injectable()
export class AuthGuard implements CanActivate {
constructor() {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
// return this.typeSelector(SigninComponent.loginTypeProperty);
let isLogged: boolean = AuthService.loggedIn;
window.alert('isLogged =' + isLogged);
return isLogged;
}
}
這是另一種情況,我必須更改密碼:
import { Injectable, Inject, Optional } from "@angular/core";
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router";
import { Observable } from "rxjs/Rx";
import { NewPasswordAuthService } from "./new-password.auth.service";
import { Router, ActivatedRoute } from '@angular/router';
import { IPwCookie } from './IPwCookie.interface';
@Injectable()
export class NewPasswordGuard implements CanActivate {
constructor(private authService: NewPasswordAuthService, private router: Router, private route: ActivatedRoute,)
{
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
console.log('query params' + JSON.stringify(route.params));
let hash:string = route.params.hash;
let userId: number = route.params.userId;
console.log('query params string' + JSON.stringify(route.params.hash));
return this.authService.activate(hash)
.map(
(): boolean => {
console.log('check passed =' + NewPasswordAuthService.isAuthenticated());
let passwordCookie:IPwCookie = {
hash: hash,
userId: userId
};
localStorage.setItem('password_hash', JSON.stringify(passwordCookie));
return NewPasswordAuthService.isAuthenticated();
},
(error) => {console.log(error)}
);
}
}
據我所知,你應該嘗試在map函數中添加一個返回類型,這是給我帶來問題的一件事。 如果不向map函數添加返回類型,則在此上下文中它不起作用。
所以只需要做.map(():boolean => {} ...)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.