Reputation: 13
I have an HttpClient injected into a service. I have a BehaviorSubject set up to track changes to that value. I am running a mock db with json-server. My service is able to execute the get() correctly when I give it something that exists, but if I give it something that doesn't exist it will give a 404. I want it to execute a post().
For a real world explanation: the user has a calendar with a daily log for each day. If they select a day where a log exists, it will get() it. But if they select a day where one does not exist, the service should post() a blank one on the server. Going back to that day will get() the blank one created earlier.
This is what I have so far:
getCurrentDay(id: string) {
this.http.get<DailyLog>(this.daysUrl + "/" + id).subscribe(data => {
this._currentDailyLog.next(Object.assign({}, data));
},
error => {
return console.log('Could not load daily log');
}
)
}
For future searchers, here is the answer I got with help from @Poul Krujit. I had to change 2 things from his suggestion: the url for the post cannot contain the /id like the get() and you need to give the object as the 2nd parameter in the post().
getCurrentDay(id: string) {
const url = `${this.daysUrl}/${id}`;
this.http.get<DailyLog>(url).pipe(
catchError(error =>
error.status === 404
? this.http.post<DailyLog>(this.daysUrl, { "id": id, })
: throwError(error)
)
).subscribe({
next: (data) => this._currentDailyLog.next({ ...data }),
error: (error) => console.log('Could not load daily log:', error)
});
}
Upvotes: 1
Views: 754
Reputation: 71931
You can use the catchError
operator:
getCurrentDay(id: string) {
const url = `${this.daysUrl}/${id}`;
this.http.get<DailyLog>(url).pipe(
catchError((error) => error.status === 404
? this.http.post<DailyLog>(url)
: throwError(error)
)
).subscribe({
next: (data) => this._currentDailyLog.next({ ...data })),
error: (error) => console.log('Could not load daily log:', error)
});
}
Upvotes: 1
Reputation: 8859
As a rule of thumb, you should not subscribe to an Observable within a service. This makes the data (or error) inaccessible by the caller. Instead of subscribing, you should use operators and return an Observable.
Let's refactor your service
export class SomeService {
getCurrentDay(id: string) {
return this.http.get<DailyLog>(this.daysUrl + "/" + id)
.pipe(
/** catchError will be called if an exception is thrown
/* and it expects you to return another Observable
/* which is a great place to make another call
* I assume post method will return newly created instance
*/
catchError(err => this.http.post<DailyLog>(this.daysUrl + "/" + id)),
// also if you need to do some stuff within your service, you can use tap
tap(data => this._currentDailyLog.next(data))
);
}
}
What the method above does is to try to retrive the data, if an error occures it will call post
api and return the result of post
. Also, update the BehaviorSubject
along the way.
NOTE: Since the service does not subscribe anymore, caller of this method has to subscribe to make everything run (Observables are lazy by default and do not execute until subscribed)
Upvotes: 0