I have an API call that returns data about the current user (i.e., username, full name, authorization group memberships, email address, etc.). I would like to only call this API once per user session and have its data shared among all components.
I don't want to rely upon localStorage, since I don't want this data to be modifiable by the user. I am trying to use a BehaviorSubject and have tried following a number of examples, but I keep receiving the initial value (null) when trying to access it via my components.
// 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
}
I have also tried using an async function for userDataValue():
async userDataValue(): Promise<UserData> {
return this.userDataSubject.value;
}
And modified my component:
async ngOnInit() {
this.userData = await this.utils.userDataValue();
console.log(this.userData); // Still returns null
console.log(await this.utils.userDataValue()); // Still returns null
}
So far, I can only make this work if I avoid using a BehaviorSubject and call the API in each component, but that seems like it shouldn't be necessary. I'd greatly appreciate any guidance in how I can make this work. Thanks.
Problem is that setUserData()
is an async function and it takes some time to this.httpService.getUserData()
to fetch your data from backend. So when your component calls it ngOnInit()
method userDataSubject
hasn't yet received the new value and that's why it returns null. I would suggest using BehaviorSubject subscribe
handler so your code will do what it is supposed to do at the right time. It would be something like:
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);
}
and in your component
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
});
}