Angular how to avoid that errors break valueChanges

Viewed 1180

In my angular 6 application, I have a problem with some rxjs operators. I have a search field and I need to call my service (service do GET request) when the user is typing in the field, it is working if the service doesn't have any errors like 500.

But a request with 500 error break the chain in the value change, probably I don't return the correct value from the service...

this is the search field in the component:

this.manufacturer.valueChanges
  .pipe(
    filter((value: string) => (value ? value.length > 2 : false)),
    tap(() => (this.loadingAutocomplete = true)),
    debounceTime(500),
    switchMap(val => this.frameService.getItems(val, "manufacturerAutocomplete")),
    tap(() => (this.loadingAutocomplete = false)),
    catchError(err =>
      return of([]))
  )
  .subscribe(
    manufacturers => {
      this.matchingManufacturers.next(manufacturers);
    },
    err => {
      this.loadingAutocomplete = false;
      this.matchingManufacturers.next([]);
    }
  );

And this is the service:

getItems(value: string, endPoint: string) {
  return this.http.get(
    this.endPointUrlService.cutLinks(this.endPointUrlService.map.get("frames")) + "/search/" + endPoint
  ).pipe(
    map((res: string[]) => {
      if (res) {
        return res;
      } else {
        return [];
      }
    })
  );
}

First time user types something it works, if I have an erros the valueChanges is not invoked again. I need to handle the errors in the component, not in the service.

3 Answers

The observable that results from your rxjs statement on the valueChanges property is completed, and that is why it doesnt react anymore.

Important: an observable that errors, will be completed, and there is nothing that you can do to alter that behavior.

What you need to do is prevent the error from your endpoint service from flowing down into the switchMap statement, and further into the chain. If it flows into the chain, that chain will be be completed. To prevent the error from flowing down, you need to return empty() when your api call throws an error.

getItems(value: string, endPoint: string) {
    return this.http.get(this.endPointUrlService.cutLinks(this.endPointUrlService.map.get("frames")) + "/search/" + endPoint).pipe(
      map((res: string[]) => {
        if (res) {
          return res;
        } else {
          return [];
        }
      }),
      catchError(err => {
          // report error to user
          console.log(err);
          // Important: stop observable from emitting anything
          return empty();
      })
    );
}

More info on the subject: http://www.syntaxsuccess.com/viewarticle/error-handling-in-rxjs

Catch error earlier - in the service layer, like this:

 getItems(value: string, endPoint: string) {
        return this.http.get(this.endPointUrlService.cutLinks(this.endPointUrlService.map.get("frames")) + "/search/" + endPoint).pipe(
          map((res: string[]) => {
            if (res) {
              return res;
            } else {
              return [];
            }
          }),
          catchError(err => return of([]))
    );
  }

Probably this method solve my problem, this is the component, and the service doesn't catch anything:

this.manufacturer.valueChanges
  .pipe(
    filter((value: string) => (value ? value.length > 0 : false)),
    tap(() => (this.loadingAutocomplete = true)),
    debounceTime(500),
    switchMap(val =>
      this.frameService.getItems(val, "manufacturerAutocomplete").pipe(
        catchError(err => {
          handleError(err);
          this.loadingAutocomplete = false;
          return [];
        })
      )
    ),
    tap(() => (this.loadingAutocomplete = false))
  )
  .subscribe(
    manufacturers => {
      this.matchingManufacturers.next(manufacturers);
    },
    err => {
      this.loadingAutocomplete = false;
      this.matchingManufacturers.next([]);
    }
  );
Related