Reputation: 1870
I have a code that fetches book by its id
const fetchBook = (bookId: number) => {
const title = 'Book' + bookId;
// mimic http request
return timer(200).pipe(mapTo({ bookId, title }));
}
const bookId$ = new Subject<number>();
const book$ = bookId$.pipe(
switchMap(bookId => fetchBook(bookId)),
shareReplay(1)
);
book$.subscribe(book => console.log('book title: ', book.title))
bookId$.next(1);
I have an API method that patches values and returns the updated object:
const patchBook = (bookId: number, newTitle: string) => {
return timer(200).pipe(mapTo({ bookId, title: newTitle }));
}
What should I do to get book$
to emit the new value after I call patchBook(1, 'New Book Title')
?
I can declare book$
as Subject
explicitly and update it manually. But it will be imperative, not reactive approach.
Upd: The patch is called as a result of user action at any time (or never)
Upd2: Actually book$
can be also changed on server side and my real code looks like this:
const book$ = combineLatest([bookId$, currentBookChangedServerSignal$]).pipe...
Upvotes: 3
Views: 155
Reputation: 8052
The same thing you did to transform a bookId into a Book, you can use to transform a Book into a patchBook.
const book$ = bookId$.pipe(
switchMap(bookId => fetchBook(bookId)),
mergeMap(({bookId, title}) => patchBook(bookId, title)),
shareReplay(1)
);
patch
is not always calledThere are many ways this could be done and the "best" way really depends on how you've architected your system.
Lets say you dynamically create a button that the user clicks and this triggers an update event.
const patchBtn = document.createElement("button");
const patchBook$ = fromEvent(patchBtn, 'click').pipe(
switchMap(_ => patchBook(bookId, title))
);
const basicBook$ = bookId$.pipe(
switchMap(bookId => fetchBook(bookId))
);
const book$ = merge(patchBook$, basicBook$).pipe(
shareReplay(1)
);
You probably want your fromEvent
events to emit some data rather then hard-coding (bookId, title)
into the stream from a click, but you get the idea. That's just one of many ways to get the job done.
And of course, it should almost always be possible (and desirable) to remove bookId$
, and replace it with a more reactive-style mechanism that hooks declarativly into whatever/wherever the ID's come from in the first place.
Upvotes: 4
Reputation: 1852
You can declare a fetchBook$
observable, and a patchBook$
subject. Then your book$
observable can be a merge of the two.
const patchBook = (bookId: number, newTitle: string) => {
return timer(200).pipe(
mapTo({ bookId, title: newTitle }),
tap(newBook=>this.patchBook$.next(newBook))
);
}
const bookId$ = new Subject<number>();
const fetchBook$ = bookId$.pipe(
switchMap(bookId => fetchBook(bookId)),
shareReplay(1)
);
const patchBook$ = Subject<{ bookId: number, newTitle: string}>();
const book$ = merge(fetchBook$, patchBook$);
book$.subscribe(book => console.log('book title: ', book.title))
bookId$.next(1);
patchBook(2, 'Moby Dick');
Upvotes: 1