我有一个应用程序在哪里注销用户,我必须点击一个REST端点。在我的角度2前端,我有以下服务来处理这个:
@Injectable()
export class AuthService {
private authEvents: Subject<boolean>;
constructor(private http: Http) {
this.authEvents = new Subject<boolean>();
}
login(email: string, password: string): Observable<Response> {
const url = 'rest/auth/login';
const body = {
email: email,
password: password
};
return this.http.post(url, body)
.do((res: Response) => {
localStorage.setItem('currentUser', JSON.stringify(res.json()));
this.authEvents.next(true);
});
}
logout(): Observable<Response> {
const url = 'rest/auth/logout';
return this.http.post(url, {})
.do((res: Response) => {
localStorage.removeItem('currentUser');
console.log('hereee!');
this.authEvents.next(false);
});
}
isSignedIn(): boolean {
return localStorage.getItem('currentUser') !== null;
}
get events(): Observable<boolean> {
return this.authEvents;
}
}
让我解释一下我在这里要做的事情。我有一个authEvents
主题,我在我的组件中订阅了这样的主题:
ngOnInit() {
this.isSignedIn = this.authService.isSignedIn();
this.authService.events.subscribe(() => {
this.isSignedIn = this.authService.isSignedIn();
});
}
当用户点击退出按钮时,我在此组件中调用以下函数:
logout() {
this.authService.logout()
.subscribe(() => {
this.router.navigate(['/home']);
}, e => this.handleError(e));
}
问题是即使注销调用尚未完成,浏览器也会重定向到home。如此有效,有时候前端会成功退出,有时则不会!我在这里做错了什么?
注意:自http呼叫发生以来,后端会话始终会注销。我还想看看如何使用Observables(和一般的rxjs)来实现这一点,尽管如果太麻烦,可以使用Promises。
答案 0 :(得分:2)
根据Subject
您的情况,只有订阅者才会收到排放,而不是未来的订阅者。我的猜测是这是你的问题 - 为了让你的生活更轻松,你可以使用BehaviorSubject
,它可以为任何未来的订阅者重新发布它的当前价值,所以你不必担心关于任何比赛条件。
但是:严格来说,它不再是一个事件,而是一个更多的状态,这是我建议你在你的情况下使用的 - 因为使用RxJS你可以利用权力国家对事件的看法。
@Injectable()
export class AuthService {
public currentUser$: BehaviorSubject<IUser> = new BehaviorSubject(null); // initial state is "null" => user is not logged in
private isSignedIn$: Observable<boolean> = currentUser$.map(user => Boolean(user)).share();
constructor(private http: Http) {
this.authEvents = new Subject<boolean>();
}
login(email: string, password: string): Observable<Response> {
const url = 'rest/auth/login';
const body = {
email: email,
password: password
};
return this.http.post(url, body)
.do((res: Response) => {
localStorage.setItem('currentUser', JSON.stringify(res.json()));
this.currentUser$.next(res.json()); // will automatically trigger the logged-in state as well
});
}
logout(): Observable<Response> {
const url = 'rest/auth/logout';
return this.http.post(url, {})
.do((res: Response) => {
localStorage.removeItem('currentUser');
console.log('hereee!');
this.currentUser$.next(null); // will automatically trigger the logged-in state as well
});
}
}
在您的组件中
private desotryed$: Subject<boolean> = new Subject();
ngOnInit() {
this.authService.isSignedIn$
.takeUntil(this.desotryed$) // to prevent memory leaks through a perpetual subscription
.subscribe(isSignedIn => {
this.isSignedIn = isSignedIn;
});
}
ngOnDestroy() {
this.desotryed$.next(true);
}
由于RxJS5文档尚未包含BehaviorSubject,因此这里是指向旧文档的链接:https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/behaviorsubject.md
答案 1 :(得分:0)
这对我有用:
正如@olsn建议的那样,我将Subject
更改为BehaviorSubject
。
但似乎出错的是组件的html。
<li routerLinkActive="active"><a href="#" (click)="logout(); $event.preventDefault()">Logout</a></li>
我必须在$event.preventDefault()
事件中添加 (click)
调用,所有内容似乎都按预期工作。虽然有人可以向我解释为什么会这样,但这会很棒!