Reputation: 15
I am trying to implement a general http handler error, but I found a strager behavior. My Error component only update the message error only after second click, but I dont know why.
The code is available here in StackBlitz: enter link description here
In app.component.ts on click I try a http request and it will fail, just to throw an error.
onClick() {
this.http
.get('http://localhost:8080/test')
.pipe(catchError(this.errorHandler))
.subscribe(() => {});
}
errorHandler(error: HttpErrorResponse) {
return throwError(error.message);
}
All errors are handled in global.error.ts and each error is add in error.service.ts:
export class GlobalErrorHandler implements ErrorHandler {
constructor(private errorService: ErrorService) {}
handleError(error: Error) {
this.errorService.add('New Error');
}
}
The error.service.ts uses BehaviorSubject and notify my error.component.ts:
export class ErrorService {
public notification = new BehaviorSubject<string>('Initial Value');
add(err: string) {
this.notification.next(err);
}
}
Finally, the error.component.ts must update the message error on screen, but it works only in the second click, but the console.log works perfect.
export class ErrorComponent implements OnInit {
public errorMessage: string = '';
constructor(public errorService: ErrorService) {}
ngOnInit() {
this.errorService.notification.subscribe({
next: (notification) => {
console.log('Error Component');
this.errorMessage = notification;
},
});
}
}
Questions:
Upvotes: 0
Views: 330
Reputation: 10944
You just need to force change detection using the ChangeDetectorRef
constructor(
public errorService: ErrorService,
private cd: ChangeDetectorRef
) {}
ngOnInit() {
this.errorService.notification.subscribe({
next: (notification) => {
console.log('Error Component');
this.errorMessage = notification;
this.cd.detectChanges();
},
});
}
https://stackblitz.com/edit/angular-ivy-tzt7xk?file=src/app/error/error.component.ts
Alternatively, wrapping the .next()
call with NgZone.run()
also lets change detection pick up the changes within its subscriptions.
constructor(private ngZone: NgZone) {}
public notification = new BehaviorSubject<string>('Initial Value');
add(err: string) {
this.ngZone.run(() => {
this.notification.next(err);
});
}
I'd suggest just displaying the subject directly with the async
pipe as well.
{{ errorService.notification | async }}
https://stackblitz.com/edit/angular-ivy-wknur7?file=src/app/error.service.ts
More information here: Angular 6 View is not updated after changing a variable within subscribe.
In summary, Angular only updates the view if the variable is changed within the "Angular Zone". The second time you press the button, it is just then picking up the first value, not the second one.
Upvotes: 2