问题描述
我有一个用户服务,该服务允许登录,注销并维护有关当前登录用户的数据:
I have a user service which allows login, logout and maintains data about the currently logged in user:
user$ = this.http.get<User>('/api/user')
.pipe(
shareReplay(1),
);
我正在使用 shareReplay(1)
,因为我不想多次调用该Web服务.
I am using shareReplay(1)
because I do not want the webservice to be called several times.
为简单起见,在其中一个组件上,我有这个功能,但是如果用户登录,我还有其他几件事要做:
On one of the components, I have this (for simplicity), but I have several other things I want to do if a user is logged in:
<div *ngIf="isUserLoggedIn$ | async">Logout</div>
isUserLoggedIn$ = this.userService.user$
.pipe(
map(user => user != null),
catchError(error => of(false)),
);
但是, isLoggedIn $
在用户登录或注销后不会更改.刷新页面时确实会更改.
However, the isLoggedIn$
does not change after the user logs in or logs out. It does change when I refresh the page.
这是我的注销代码:
logout() {
console.log('logging out');
this.cookieService.deleteAll('/');
this.user$ = null;
console.log('redirecting');
this.router.navigateByUrl('/login');
}
我知道,如果我将变量分配为null,则内部可观察对象不会重置.
I understand that the internal observable is not reset if I assign the variable to null.
因此,对于注销,我从以下答案中得到了提示: https://stackoverflow.com/a/56031703 关于刷新 shareReplay()
.但是,模板中使用的user $会使我的应用程序在尝试注销时立即陷入困境.
So, for logout, I took clue from this answer: https://stackoverflow.com/a/56031703 about refreshing a shareReplay()
. But, the user$ being used in the templates causes my application to go into a tizzy as soon as I attempt to logout.
在我的尝试之一中,我尝试了 BehaviorSubject
:
In one of my attempts, I tried BehaviorSubject
:
user$ = new BehaviorSubject<User>(null);
constructor() {
this.http.get<User>('/api/user')
.pipe(take(1), map((user) => this.user$.next(user))
.subscribe();
}
logout() {
...
this.user$.next(null);
...
}
除了刷新页面外,此方法效果更好.auth-guard( CanActivate
)始终将用户$作为null并重定向到登录页面.
This works a little better except when I refresh the page. The auth-guard (CanActivate
) always gets the user$ as null and redirects to the login page.
当我刚开始的时候,这似乎是一件容易的事,但是每次更改我都会陷入一个更深的洞里.有解决办法吗?
This seemed like an easy thing to do when I started out, but I am going on falling into a deeper hole with each change. Is there a solution to this?
推荐答案
对于这种情况,我将数据流(用户)与操作流(登录用户)一起使用,并使用 combineLatest
进行操作合并两个:
For scenarios like this, I use a data stream (user) with an action stream (log user in) and use combineLatest
to merge the two:
private isUserLoggedInSubject = new BehaviorSubject<boolean>(false);
isUserLoggedIn$ = this.isUserLoggedInSubject.asObservable();
userData$ = this.http.get<User>(this.userUrl).pipe(
shareReplay(1),
catchError(err => throwError("Error occurred"))
);
user$ = combineLatest([this.userData$, this.isUserLoggedIn$]).pipe(
map(([user, isLoggedIn]) => {
if (isLoggedIn) {
console.log('logged in user', JSON.stringify(user));
return user;
} else {
console.log('user logged out');
return null;
}
})
);
constructor(private http: HttpClient) {}
login(): void {
this.isUserLoggedInSubject.next(true);
}
logout(): void {
this.isUserLoggedInSubject.next(false);
}
我在这里有一个有效的stackblitz: https://stackblitz.com/edit/angular-user-logout-deborahk
I have a working stackblitz here: https://stackblitz.com/edit/angular-user-logout-deborahk
user $
是用户数据流和发出布尔值的 isUserLoggedIn $
流的组合.这样,我们可以使用映射并将返回的值映射到用户,如果用户已注销,则将其映射为null.
The user$
is the combination of the user data stream and the isUserLoggedIn$
stream that emits a boolean value. That way we can use a map and map the returned value to the user OR to a null if the user has logged out.
希望类似的东西对您有用.
Hope something similar works for you.
这篇关于RxJS shareReplay()不会发出更新的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!