jelliott8020
jelliott8020

Reputation: 13

Angular HttpClient: If get() fails, then execute a post()

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

Answers (2)

Poul Kruijt
Poul Kruijt

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

Bunyamin Coskuner
Bunyamin Coskuner

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

Related Questions