MARKAND Bhatt
MARKAND Bhatt

Reputation: 2650

RxJs : How to rethrow error from observable

I am new to RxJS. Learning observables and subjects. I have a very simple login code divided into two files, a) auth.service.ts and b) login.component.ts

Here is login.component.ts

public login(username: string, password: string): Observable<boolean> {
const headers = { 'content-type': 'application/json' }
const reqBody = {
  "userName": username,
  "password": password
};
const body = JSON.stringify(reqBody);
const subject = new Subject<boolean>();
this.httpClient.post<IAuthToken>(this.apiUrl, body, { 'headers': headers })
  .subscribe(response => {
    if (response) {
      localStorage.setItem(this.accessTokenKey, response.access_token);
      subject.next(true);
    }
    else {
      throwError("Login request failed. Please try again later");
    }
  },
    error => {
      // TODO : Code to show invalid username password
      // this code throws error. Subscribe in the login.component.ts doesnt receive any error.
      throwError(error.error);
    });
return subject.asObservable();

}

Here is login.component.ts code

errorMsg: string;
constructor(
    private authenticationService: AuthService
  ) {  }
async onLogin(): Promise<void> {
   
         
        this.authenticationService.login(this.loginForm.value.username,
          this.loginForm.value.password)
          .subscribe((response) => {
            this.errorMsg = null;
            if (response)
                 console.log("logged In");
          },
            error => {
              // TODO : Code to show invalid username password
              // This code doesnt execute, i.e. error is not caught.
              this.errorMsg = error.error;
              console.log(error);
            });
    }
  }

I want to throw error for invalid username password from auth.service.ts and catch it in login.component.ts so that can bind the error to UI. However, the throwError throws error from auth.service.ts, but the error isnt caught in login.component.html

Upvotes: 2

Views: 4242

Answers (2)

Lazar Ljubenović
Lazar Ljubenović

Reputation: 19764

Several misconceptions here.

Firstly, throwError is a function which returns an observable that immediately errors. Simply calling it like you do doesn't do anything.

throwError(error.error); // creates an unused observable

Secondly, you're subscribing in the service with some weird glued-together code with a subject. Your service is supposed to just create an observable, and then you subscribe to it in the component. The error would then be caught correctly by default.

// in service
logIn (username: string, password: string): Observable<boolean> {
  return this.http.post(api, { username, password })
    .pipe(mapTo(true))
}

// in component
onSubmit () {
  this.service.logIn('username', 'p@ssword')
    .subscribe({
      next: () => {
        console.log('Success!')
      },
      error: (error) => {
        console.error(error)
      }
    })
}

throwError is usually used in conjuction with catchError when you want to propagate some errors and handle some errors.

return this.http.post(api, { username, password })
  .pipe(
     catchError((error) => {
       if (canHandleError(error)) {
         // handle...
         return of(false)
       } else {
         return throwError(error)
       }
     }),
  )

Upvotes: 4

zainhassan
zainhassan

Reputation: 1780

No need to subscribe in service

public login(username: string, password: string): Observable<boolean> {
    const headers = { 'content-type': 'application/json' }
    const reqBody = {
        "userName": username,
        "password": password
    };
    const body = JSON.stringify(reqBody);
    return this.httpClient.post<IAuthToken>(this.apiUrl, body, { 'headers': headers })
        .pipe(map(response => {
            if (response) {
                localStorage.setItem(this.accessTokenKey, response.access_token);
                return true;
            }
        }))
}

Upvotes: 1

Related Questions