Handling Errors in RxJS: A guide to propagating errors from observables

I recently started learning RxJS and am currently focusing on observables and subjects. I have a basic login code that is split into two separate files: a) auth.service.ts and b) login.component.ts

Below is the content of 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();

}

And here is the code for the login.component.ts:

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 am trying to throw an error for an invalid username or password from auth.service.ts and catch it in login.component.ts so that I can display the error message on the UI. However, when I use throwError in auth.service.ts, the error is not being caught in login.component.html.

Answer №1

There are a few misunderstandings in the code.

Firstly, it's important to note that throwError is a function that returns an observable which immediately throws an error. Just calling it without handling the returned observable does not have any effect.

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

Additionally, the service seems to have a confusing setup with subscribing and a subject. A service should typically only create observables, while the subscription and error handling should be done in the component. This separation allows for correct error handling by default.

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

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

throwError is commonly used together with catchError when you want to handle certain errors while propagating others.

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

Answer №2

There is no requirement to sign up for the service

function signIn(username: string, password: string): Observable<boolean> {
    const headers = { 'content-type': 'application/json' }
    const requestBody = {
        "username": username,
        "password": password
    };
    const body = JSON.stringify(requestBody);
    return this.httpClient.post<AuthTokenResponse>(this.apiUrl, body, { 'headers': headers })
        .pipe(map(response => {
            if (response) {
                localStorage.setItem(this.tokenKey, response.access_token);
                return true;
            }
        }))
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Encountering a ng-select2 Error with Angular version 4.1.3

I have recently installed the ng-select2 package, but I encountered an error when trying to compile my code using 'ng serve'. Node version: 8.10.0 NPM version: 6.0.0 Another list item Operating System: Windows 7 ERROR in d:/PATH-TO-PROJECT-F ...

What steps are involved in setting up nginx as the server for an Angular 6 application?

I am looking to access the index.html file for my application running on nginx, and then access the application built with angular-cli (angular 6). Everywhere I look, it mentions creating the angular application and starting the server. I am a bit confus ...

Angular 5 does not recognize the function submitEl.triggerEventHandler, resulting in an error

Greetings! I am currently working on writing unit test cases in Angular5 Jasmine related to submitting a form. Below is the structure of my form: <form *ngIf="formResetToggle" class="form-horizontal" name="scopesEditorForm" #f="ngForm" novalidate (ngSu ...

Event listener for scrolling in Angular Dart component

I am looking to implement a functionality where a "back to top" button appears once the user has scrolled the page by 500px. I have been trying to capture the scroll event in the main Div of my AppComponent. <div class="container" scrollable&g ...

Error encountered when pushing Angular data to Django for user login form: 'Unexpected token < in JSON at position 2' while attempting to parse the JSON

I believe the < symbol is appearing because the response is in HTML or XML format. This is the section of my code where the login process is failing. public login(user) { this.http.post('/api-token-auth/', JSON.stringify(user), this.ht ...

Troubleshoot: Child element not displaying when using *ngIf in Ionic 3

I'm working on an app with Ionic 3 that includes animations. After installing the necessary package: npm install --save CSS-animator Below is a snippet from my template: <div class="alert" *ngIf="!show" #myElement> <h5>Here is my ani ...

Designing the Pull to Refresh feature in Nativescript Angular

I've been experimenting with the Nativescript Angular Pull to Refresh Styling demo (Link) and I'm running into an issue where this.myListViewComponent remains undefined, causing the following snippet not to trigger: if (this.myListViewComponent ...

Setting up Material-UI for React in conjunction with Typescript: A step-by-step guide

I've encountered issues while trying to integrate Material UI into my React project that's written in Typescript. Following the tutorial, I began by adding the react-tab-event-plugin. import injectTapEventPlugin from 'react-tap-event-plugi ...

Learn how to host a singular instance of an Angular application for every unique route, similar to the setup utilized in meet.jit.si

Is there a way to create an Angular app that loads a new instance of itself on every route/url? For example, visiting http://happy-app.com/happy would show the app in one state, and going to http://happy-app.com/happier would load the same app in a differe ...

Can the default setting for --base-href be configured specifically for ng build and not for ng serve?

How can the default setting of --base-href for ng build be adjusted without impacting ng serve? Utilizing Angular CLI version 1.6.6 Compatible with Angular 5 and above ...

Unit testing Angular components can often uncover errors that would otherwise go unnoticed in production. One common

I need assistance writing a simple test in Angular using Shallow render. In my HTML template, I am utilizing the Async pipe to display an observable. However, I keep encountering the following error: Error: InvalidPipeArgument: '() => this.referenc ...

How to utilize *ngFor alongside the async pipe for conditional rendering in Angular 8 HTML

.html <ng-container *ngFor="let contact of listContact | async; let index = index;"> <h6 class="title" *ngIf="contact && contact['type']"> {{contact['type']}} </h6> <div> {{conta ...

Having trouble installing memlab using the npm package

Recently, I made an attempt to install the memlab library from Meta's GitHub. Initially, when I installed it without using the -g flag, the installation was successful. However, I encountered an issue where I could not execute any of the memlab comman ...

What is the best way to iterate through an object using NgFor?

One scenario involves managing multiple students with various details such as name, email, phone, etc., along with a note object: { "_id" : ObjectId("5b9551a60e89ad15a8ff77fb"), "name" : "RICHE", "note" : [ { "subject" : " ...

Angular8 Chart.js customizes the Y axis tick labels

Is there a way to dynamically adjust the Y-axis ticks in a chart.js graph? I attempted to modify them using the following commands: this.chartOptions.scales.yAxes[0].ticks.min = 10; this.chartOptions.scales.yAxes[0].ticks.max = 18; ...

Apply criteria to an array based on multiple attribute conditions

Given an array containing parent-child relationships and their corresponding expenses, the task is to filter the list based on parents that have a mix of positive and negative expenses across their children. Parents with only positive or negative child exp ...

Unable to interact with Span in a table cell - Protractor/Typescript error

I am facing an issue where clicking on the Span Element within a Grid cell is not working. I have tried using actions and the code below, but neither worked for me. Any advice would be greatly appreciated. async SaveorCancelRow() { var table = this. ...

Efficiently linking styles through computation in Angular

So I've been working with Angular5 and I figured out how to bind an HTML element's style to a Boolean value. However, I'm struggling to find a way to do this for multiple styles simultaneously. For example, I have this code snippet that wor ...

Converting data types of a destructured property

In my Next.js application, I'm using a router hook and destructuring like this: const { query: { run_id }, } = useRouter(); The type of `run_id` is as follows: run_id: string | string[] | undefined I tried to typecast it as shown below, but it doe ...

How can I display and link a base64 string to an Image as a source in Nativescript?

I'm having trouble displaying and binding a base64 image as an ImageSource in my View. The image doesn't show up at all, and I couldn't find any helpful information about it in the documentation. Am I missing something? The imageSource prop ...