Angular 7 - 使用 BehaviorSubject 向所有组件发布 API 结果
Angular 7 - Use BehaviorSubject for Publishing API Results to All Components
我有一个 API 调用 returns 当前用户的数据(即用户名、全名、授权组成员身份、电子邮件地址等)。我只想在每个用户会话中调用此 API 一次,并在所有组件之间共享其数据。
我不想依赖 localStorage,因为我不希望用户可以修改这些数据。我正在尝试使用 BehaviorSubject 并尝试遵循许多示例,但在尝试通过我的组件访问它时,我一直收到初始值 (null)。
// HTTP Service:
getUserData() {
const url = `${baseUrl}/api/users/`;
return this.httpClient.get<UserData>(url).toPromise();
}
// Utils Service
export class UtilsService {
private userDataSubject: BehaviorSubject<UserData>;
constructor(
private httpService: HttpService,
) {
this.userDataSubject = new BehaviorSubject<UserData>(null);
this.setUserData();
}
async setUserData() {
let userData: UserData = await this.httpService.getUserData();
this.userDataSubject.next(userData);
console.log(this.userDataSubject.value); // Properly returns the data I want
}
public get userDataValue(): UserData {
return this.userDataSubject.value;
}
// Component
ngOnInit() {
this.userData = this.utils.userDataValue; // This is the variable I need to set in my component.
console.log(this.userData); // Returns null
console.log(this.utils.userDataValue); // Also returns null
}
我也尝试过为 userDataValue() 使用异步函数:
async userDataValue(): Promise<UserData> {
return this.userDataSubject.value;
}
并修改了我的组件:
async ngOnInit() {
this.userData = await this.utils.userDataValue();
console.log(this.userData); // Still returns null
console.log(await this.utils.userDataValue()); // Still returns null
}
到目前为止,我只能在避免使用 BehaviorSubject 并在每个组件中调用 API 的情况下才能完成这项工作,但这似乎没有必要。我将非常感谢任何有关如何完成这项工作的指导。谢谢。
问题是 setUserData()
是一个异步函数,this.httpService.getUserData()
需要一些时间才能从后端获取数据。因此,当您的组件调用它 ngOnInit()
方法时,userDataSubject
尚未收到新值,这就是它 returns 为空的原因。我建议使用 BehaviorSubject subscribe
处理程序,这样您的代码就会在正确的时间执行它应该执行的操作。它会是这样的:
export class UtilsService {
public userDataSubject: BehaviorSubject<UserData>;
constructor(private httpService: HttpService) {
this.userDataSubject = new BehaviorSubject<UserData>(null);
this.setUserData();
}
async setUserData() {
let userData: UserData = await this.httpService.getUserData();
this.userDataSubject.next(userData);
}
并在您的组件中
ngOnInit() {
this.utils.userDataSubject.subscribe((data) => {
if(data === null)
return;
console.log(data);
// component logic to use your data
// this will be called every time userDataSubject receives a new value
});
}
这里是的一点改进:
您应该使用尺寸 1 的 ReplaySubject<>
而不是 BehaviorSubject<>
。为什么?
BehaviorSubject
必须用一个值初始化。在您的代码中,它是 null
。这意味着:所有订阅者都必须处理 null
(就像在 alexortizl 的回答中,if(date === null)
行)。
另一方面,大小为 1 的 ReplaySubject
将像 BehaviorSubject
一样工作,但没有初始值。在发出第一个正确的值之前,所有订阅者都不会获得任何值。阅读本文以进行更深入的研究 ->
因此,您的代码将如下所示:
服务
export class UtilsService {
// we set the type to "Subject" to hide the actual ReplaySubject
public userDataSubject$: Subject<UserData> = new ReplaySubject(1);
constructor(private httpService: HttpService) {
this.setUserData();
}
async setUserData() {
let userData: UserData = await this.httpService.getUserData();
this.userDataSubject$.next(userData);
}
Component.ts
ngOnInit() {
this.utils.userDataSubject$.subscribe((data) => {
console.log(data);
// component logic to use your data
// this will be called every time userDataSubject receives a new value
});
}
Component.html
... 或使用 async
-管道更好。然后你不必取消订阅,它可以提高 Angular 的更改检测。深潜 -> “SEEING” ANGULAR CHANGE DETECTION IN ACTION
<ng-container *ngIf="utils.userDataSubject$ | async as userData">
<span>Hello {{ userData.firstName }} {{ userData.lastName }}</span>
</ng-container>
我有一个 API 调用 returns 当前用户的数据(即用户名、全名、授权组成员身份、电子邮件地址等)。我只想在每个用户会话中调用此 API 一次,并在所有组件之间共享其数据。
我不想依赖 localStorage,因为我不希望用户可以修改这些数据。我正在尝试使用 BehaviorSubject 并尝试遵循许多示例,但在尝试通过我的组件访问它时,我一直收到初始值 (null)。
// HTTP Service:
getUserData() {
const url = `${baseUrl}/api/users/`;
return this.httpClient.get<UserData>(url).toPromise();
}
// Utils Service
export class UtilsService {
private userDataSubject: BehaviorSubject<UserData>;
constructor(
private httpService: HttpService,
) {
this.userDataSubject = new BehaviorSubject<UserData>(null);
this.setUserData();
}
async setUserData() {
let userData: UserData = await this.httpService.getUserData();
this.userDataSubject.next(userData);
console.log(this.userDataSubject.value); // Properly returns the data I want
}
public get userDataValue(): UserData {
return this.userDataSubject.value;
}
// Component
ngOnInit() {
this.userData = this.utils.userDataValue; // This is the variable I need to set in my component.
console.log(this.userData); // Returns null
console.log(this.utils.userDataValue); // Also returns null
}
我也尝试过为 userDataValue() 使用异步函数:
async userDataValue(): Promise<UserData> {
return this.userDataSubject.value;
}
并修改了我的组件:
async ngOnInit() {
this.userData = await this.utils.userDataValue();
console.log(this.userData); // Still returns null
console.log(await this.utils.userDataValue()); // Still returns null
}
到目前为止,我只能在避免使用 BehaviorSubject 并在每个组件中调用 API 的情况下才能完成这项工作,但这似乎没有必要。我将非常感谢任何有关如何完成这项工作的指导。谢谢。
问题是 setUserData()
是一个异步函数,this.httpService.getUserData()
需要一些时间才能从后端获取数据。因此,当您的组件调用它 ngOnInit()
方法时,userDataSubject
尚未收到新值,这就是它 returns 为空的原因。我建议使用 BehaviorSubject subscribe
处理程序,这样您的代码就会在正确的时间执行它应该执行的操作。它会是这样的:
export class UtilsService {
public userDataSubject: BehaviorSubject<UserData>;
constructor(private httpService: HttpService) {
this.userDataSubject = new BehaviorSubject<UserData>(null);
this.setUserData();
}
async setUserData() {
let userData: UserData = await this.httpService.getUserData();
this.userDataSubject.next(userData);
}
并在您的组件中
ngOnInit() {
this.utils.userDataSubject.subscribe((data) => {
if(data === null)
return;
console.log(data);
// component logic to use your data
// this will be called every time userDataSubject receives a new value
});
}
这里是
您应该使用尺寸 1 的 ReplaySubject<>
而不是 BehaviorSubject<>
。为什么?
BehaviorSubject
必须用一个值初始化。在您的代码中,它是 null
。这意味着:所有订阅者都必须处理 null
(就像在 alexortizl 的回答中,if(date === null)
行)。
另一方面,大小为 1 的 ReplaySubject
将像 BehaviorSubject
一样工作,但没有初始值。在发出第一个正确的值之前,所有订阅者都不会获得任何值。阅读本文以进行更深入的研究 ->
因此,您的代码将如下所示:
服务
export class UtilsService {
// we set the type to "Subject" to hide the actual ReplaySubject
public userDataSubject$: Subject<UserData> = new ReplaySubject(1);
constructor(private httpService: HttpService) {
this.setUserData();
}
async setUserData() {
let userData: UserData = await this.httpService.getUserData();
this.userDataSubject$.next(userData);
}
Component.ts
ngOnInit() {
this.utils.userDataSubject$.subscribe((data) => {
console.log(data);
// component logic to use your data
// this will be called every time userDataSubject receives a new value
});
}
Component.html
... 或使用 async
-管道更好。然后你不必取消订阅,它可以提高 Angular 的更改检测。深潜 -> “SEEING” ANGULAR CHANGE DETECTION IN ACTION
<ng-container *ngIf="utils.userDataSubject$ | async as userData">
<span>Hello {{ userData.firstName }} {{ userData.lastName }}</span>
</ng-container>