1

I'd like to know the best way to avoid nested subscriptions in the following scenario. I have an API rest call which can return a 401 unauthorized status code if the user token is expired. This is handled in the error callback of the subscription. When the error callback is called it ever opens an angular material dialog but only when the error received has the 401 code the user must be redirected to the login page on dialog close event. For the last purpose I subscribe to the afterClosed() stream of the dialogRef instance:

this.api.fetchData().subscribe({
                         next: (data) => {
                            // do something with data
                        },
                        error: (e: {status_code: number, message: string}) => {
                            const dialogRef = this.dialog.open(DialogErrorComponent, {
                                data: {text: e.message},
                                panelClass: 'my-dialog'
                            });
                            if (e.status_code === 401) {
                                dialogRef.afterClosed().subscribe((res) => {
                                    this.router.navigate(['login']);
                                })
                            }
                        }
                    });

How can I refactor that code to avoid the nested subcriptions but keeping the purpose to subscribe to the afterClosed() only if the condition is met in the error callback?

1 Answer 1

1

The following is (to me, at last) the most "Refactor without changing the semantics at all" solution.

I'm catching the error and transforming it into a stream that never emits anything. This isn't really much better than the nested subscribe that you use, but it does allow for some more interesting ways to manage errors in the future.

If ever you decide to re-emit certain errors, or re-try after an error, or something, that should now be pretty seamless. This would also, for example, let you return something from the dialog that could be emitted as data/error/etc.

this.api.fetchData().pipe(

  catchError((e: {status_code: number, message: string}) => {

    const dialogRef = this.dialog.open(DialogErrorComponent, {
      data: {text: e.message},
      panelClass: 'my-dialog'
    });

    return e.status_code === 401 ? dialogRef.afterClosed().pipe(
      tap(_ => this.router.navigate(['login'])),
      ignoreElements()
    ) : EMPTY;
  })

).subscribe({
  next: (data) => {/* do something with data */}
});

On the other hand, if you're pretty sure that you're never extending this... I dunno... your code looks easy enough to read/reason about.

0

Not the answer you're looking for? Browse other questions tagged or ask your own question.