I have a service in angular that gets data from an API, and this data is used in a lot of components, so calling the API to get the same data every time is a waste.
So I went ahead and tried to use BehaviorSubjects to store the data. I worked nicely to a point, using it like this:
Service:
books = new BehaviorSubject<Book[]>(null);
setBooks(book: Book[]) {
this.books.next(book);
}
getBooks() {
this.http.get(...).do(
(res) => {
this.setBooks(res.books);
}
);
}
Components:
ngOnInit() {
this.booksService.books.subscribe(
(books) => {
// in case the api was never called
if (books == null) {
return this.booksService.getBooks().subscribe();
}
console.log(books);
// ...
}
);
}
It works fine when there are books, but if the books are an empty array (which is one of the expected behaviors), the program gets stuck on a loop, calling the api forever. Testing it, I figured it gets stuck on the .do() part in the service call, but I have no clue why.
I read here BehaviorSubject is not meant to use with null initial value, but like @estus suggested, using ReplaySubject(1) the same thing happens.
I wonder if there's a "correct" way to store the data like this. I also don't think using the localstorage is the best solution since it could be a great amount of data and it changes a lot along the use of the application.
Do it this way:
Service
// init books as BehaviorSubject with an empty array of type Book
books = new BehaviorSubject<Book[]>([]);
setBooks(book: Book[]) {
this.books.next(book);
}
// return books as an Observable
getBooks(): Observable<Book[]> {
// only if length of array is 0, load from server
if (this.books.getValue().length === 0) {
this.loadBooks();
}
// return books for subscription even if the array is yet empty.
// It‘ll get filled soon.
return this.books.asObservable();
}
private loadBooks(): void {
this.http.get(...).do(
(res) => {
this.books.next(res.books);
} );
}
TS-File
ngOnInit() {
this.booksService.getBooks().subscribe(
(books) => {
console.log('books: ', books);
});
}
What happens here is, that you will only load the book list once. The subscription to books is established and as soon as the book list arrives, the new list gets propagated to all subscribers.
So you should see 2 times console log. The first with an empty list and the second with the books.