I am trying to create a custom RxJs operator that checks if the API response contains an error message, and if it is there then it throws an error with that message. Then I want to handle that error in my ticket-feedback.service.ts.
BaseServerResponse> Type:
class BaseServerResponse<T> {
data?: T;
error?: string;
message?: string;
}
Custom RxJs operator:
export const rxThrowCustomServerError = <T>() => {
return function (source: Observable<BaseServerResponse<T>>): Observable<BaseServerResponse<T>> {
return new Observable((subscriber) => {
const subscription = source.subscribe({
next(x) {
if (x.error) {
subscriber.error(x.error);
}
subscriber.next(x);
},
error(error) {
subscriber.error(error);
},
complete() {
subscriber.complete();
},
});
return () => {
subscription.unsubscribe();
};
});
};
};
ticket-feedback.component.ts:
@Component({
selector: 'ticket-feedback-list',
standalone: true,
imports: [...ANGULAR_MODULES, ...ANGULAR_MATERIAL_MODULES],
providers: [],
templateUrl: './ticket-feedback-list.component.html',
styleUrls: ['./ticket-feedback-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export default class TicketFeedbackListComponent {
private _service = inject(TicketFeedbackService);
form = this._service.getFeedbackForm;
feedbackResponse = toSignal(this._service.feedbackResponse$);
public forSubmit() {
this._service.feedbackRequested$.next({
fromDate: this.form.controls.fromDate.value,
toDate: this.form.controls.toDate.value,
retailerMobileNumber: this.form.controls.retailerMobileNumber.value,
pageNumber: 1,
pageSize: 10,
});
}
}
ticket-feedback.service.ts:
feedbackResponse$ = this.feedbackRequested$.pipe(
filter((feedbackRequest) => feedbackRequest && !isEmpty(feedbackRequest) && this.getFeedbackForm.valid),
switchMap((request) =>
this._http.get<BaseServerResponse<Feedback[]>>(appApiResources.feedback, {
params: {
pageNumber: request.pageNumber,
pageSize: request.pageSize,
fromDate: request.fromDate,
toDate: request.toDate,
'api-version': 1,
},
})
),
rxThrowCustomServerError(),
catchError(() => of(new BaseServerResponse<Feedback[]>([] as Feedback[], 0, 0, 0, '', '', 'Unable to fetch feedbacks'))),
finalize(() => this.feedbackRequested$.next({} as IGetFeedbackRequest))
);
In HTML I am simply reading the feedbackResponse Singnal as:
feedbackResponse() | json
However, this works (get API data, throw an error from custom RxJs operator, handle error in catchError in the service.) only for the first time when I click on the form submit button, but from next time onwards nothing happens not even the API call. If I remove rxThrowCustomServerError(), then the API call works every time the button is clicked.
What's the issue here?
When an observable enits an error, it completes. Therefore, when
anyObservable$.pipe(
rxThrowCustomServerError()
)
emits an error, it's the last thing it emits, then it closes, blissfully unaware that it might immediately pipe into a catchError()
operator.
Instead, try replacing
rxThrowCustomServerError(),
catchError(() => of(new BaseServerResponse<Feedback[]>([] as Feedback[], 0, 0, 0, '', '', 'Unable to fetch feedbacks'))),
with
map(o => (o?.error) ? new BaseServerResponse<Feedback[]>(
[] as Feedback[],
0,
0,
0,
'',
'',
'Unable to fetch feedbacks'
)
: o
),