I have a component that uses a service to submit some data.
This component uses ChangeDetectionStrategy.OnPush
, but something strange is happening:
When request succeeds: next
callback is invoked, formStatus
changes to 'success' and interface refreshes with this change.
When request fails, error
callback is invoked, formStatus
changes to 'error' but interface does not refresh with this change.
As I understand, interface should not refresh because I use ChangeDetectionStrategy.OnPush
and I am changing a basic type string variable.
So, ¿why change detection is triggered in next
?
When some error happens in the service, I want to receive it inside the error callback.
submit() {
this.formStatus = 'sending';
this._service.create(data).subscribe({
next: () => {
this.formStatus = 'success';
// interface refreshes
},
error: () => {
this.formStatus = 'error';
// interface does not refresh
console.log(this.formStatus); // prints 'error'
},
});
}
<div *ngIf="formStatus === 'success'">
...
</div>
Makes a call to the API and catches the error using errorUtil.handle
, which throws an rxjs Error (because I want to return Observable.error).
create(requestData: Data): Observable<Data> {
return this._http
.post<HttpResponse<CreateResponse>>(
MY_API.createUrl,
JSON.stringify(requestData)
)
.pipe(
catchError(this._errorUtil.handle()),
map(data => this.onSuccessCreate(requestData, data))
);
}
handle<T>(): (error: HttpErrorResponse, caught?: Observable<T>) => Observable<T> {
return (error: HttpErrorResponse, caught?: Observable<T>) => {
return throwError();
};
}
I have tried changing return throwError();
to throw throwError();
but the behaviour is the same.
This is a weird behaviour, I still do not understand why when next is invoked, interface is updated. It should not be updated either.
The right way to do this is using a stream to listen to changes in form status.
type FormStatus = 'clean' | 'sending' | 'success' | 'error';
// ...
private _formStatus$: BehaviorSubject<FormStatus> = new BehaviorSubject<FormStatus>('clean');
set formStatus(status: FormStatus) {
this._formStatus$.next(status);
}
get formStatus$(): Observable<FormStatus> {
return this._formStatus$.pipe(
shareReplay({ refCount: true, bufferSize: 1 })
);
}
<div *ngIf="(formStatus$ | async) !== 'error'">
...
</div>