我想为用户对象创建一个被动商店。所以让我先描述一下我的尝试。我定义了一个简单的用户类:
export class User {
constructor(public $key: string) {}
}
为简单起见,该类只包含一个键。我使用firebase存储并查询此存储我创建了一个服务类:
export class UserService {
constructor(private af: AngularFire, private authService: AuthService) {}
getUser() {
return this.authService.authInfo$.concatMap(a => this.af.database.object('/users/' + a.uid));
}
}
getUser()
首先获取用户标识并查询特定用户的firebase数据库。但是我不想直接使用该服务。相反,我想拥有一个包含最新用户对象的BehaviorSubject的全局存储。所以我们来介绍商店:
export class UserStore {
private _user: BehaviorSubject<User> = new BehaviorSubject(null);
constructor(private userService: UserService) {
this.userService.getUser().subscribe(
userJson => {
this._user.next(new User(userJson.$key, userJson.firstName, userJson.lastName, null, userJson.personalDataSheet));
},
err => console.error
);
}
get user() {
return this._user.asObservable();
}
}
现在我在这样的组件中使用这个商店:
this.userStore.user.subscribe(
user => console.log(user)
);
它有效!用户已登录。但是,当我做
之类的事情时user: User;
...
this.userStore.user.subscribe(
user => this.user = user
);
只有在用户为空时才会调用subscribe。这是为什么? this.user = user
是否会创建某种其他订阅并取消第一个订阅?
答案 0 :(得分:3)
问题是您的BehaviorSubject正在发出至少两个不同的值:
null
- 实例化主题时提供的初始值。User
instance - Firebase调用返回后立即发出的值。User
实例 - 由于所有内容都与Firebase“实时”关联,因此您的主题可能会继续为当前用户发出不同的实例(例如,如果用户登录,您将获得新值出)。因此,根据.subscribe()
的执行点,您最终可能会获得null
值。
我会尝试使用ReplaySubject。 ReplaySubject 仅在将值明确推送到其中时才开始发送。这意味着您可以删除初始null
值,只要Firebase未返回,用户将无法使用(=订阅将“等待”)。
更具体地说,这是我要更改的行:
private _user: ReplaySubject<User> = new ReplaySubject<User>(1);