ngrx / platform

Reactive State for Angular
https://ngrx.io
Other
8.01k stars 1.97k forks source link

tapResponse error parameter response returns an error observable not the error. #4390

Closed jowc closed 3 months ago

jowc commented 3 months ago

Which @ngrx/* package(s) are the source of the bug?

signals

Minimal reproduction of the bug/regression with instructions

I'm using the latest angular NGRX signals v17.2.0 and NGRX operators v17.2.0. The tapResponse operator emits an error observable instead of the error response.

The error returns an observable, I have to subscribe to the error response to utilize the error.

export const DashboardStore = signalStore(
  withState(initialState),
  withMethods((store, dashboardService = inject(DashboardService)) => ({
    getData: rxMethod<void>(
      pipe(
        tap(() => {
          patchState(store, { status: 'loading' });
        }),
        switchMap(() =>
          dashboardService.getData().pipe(
            tapResponse({
              next: (res) => {
                  return patchState(store, () => ({
                    status: 'success' as StatusState,
                    data: res?.response as DashboardResInterface,
                  }));
              },
              error: (error: any) => {
                console.error(error); //this currently logs an error observable.
                error.pipe(take(1)).subscribe((err: any) => {  // an unnecessary subscription here.
                   patchState(store, () => ({
                   status: 'error' as StatusState,
                  message: error?.message as string, // expected to grab the error response message.
                }));
                });
              },
            })
          )
        )
      )
    ),
  }))
);

Expected behavior

I intend to get the actual error object, not the error observable.

export const DashboardStore = signalStore(
  withState(initialState),
  withMethods((store, dashboardService = inject(DashboardService)) => ({
    getData: rxMethod<void>(
      pipe(
        tap(() => {
          patchState(store, { status: 'loading' });
        }),
        switchMap(() =>
          dashboardService.getData().pipe(
            tapResponse({
              next: (res) => {
                  return patchState(store, () => ({
                    status: 'success' as StatusState,
                    data: res?.response as DashboardResInterface,
                  }));
              },
              error: (error: any) => {
                console.error(error); //this currently logs an error observable.
                return patchState(store, () => ({
                  status: 'error' as StatusState,
                  // message: error?.message as string, // expected to grab the error response message.
                }));
              },
            })
          )
        )
      )
    ),
  }))
);

Versions of NgRx, Angular, Node, affected browser(s) and operating system(s)

ngrx/operators: 17.2.0 @ngrx/signals: 17.2.0

Other information

No response

I would be willing to submit a PR to fix this issue

jowc commented 3 months ago

The issue was from my interceptor error handler.

export const LoggingInterceptor = (
  req: HttpRequest<unknown>,
  next: HttpHandlerFn
): Observable<HttpEvent<unknown>> => {
  const loginService = inject(LoginService);
  const tokenService = inject(TokenService);
  if (tokenService.GetAccessToken()) {
    const modifiedReq = req.clone({
      headers: new HttpHeaders().append(
        'Authorization',
        `Bearer ${tokenService.GetAccessToken()}`
      ),
    });
    return next(modifiedReq).pipe(
      catchError((err) => {
        console.error(err);
        if (err?.status === 201) {
          loginService.deleteTokenAndRedirect();
        }
        return throwError(() => of(err)); // the issue was with this line. I fixed it by removing the of() operator.
      })
    );
  }
  return next(req);
};