Reputation: 403
I have a question about the best practice of Angular2 error handling. The is the code i use to catch an error:
Getdata(data: object){
let body = JSON.stringify(data);
let headers = new Headers({ 'Content-Type': 'application/json' });
return this.http.post('/getData', body)
.map((res) => res.json())
.catch(this._errorHandler);
}
_errorHandler(error: Response){
console.log("Error Dataservice", error);
return Observable.throw(error || "Server Error");
}
Do i Need to make a Catch for each new method or can I always use the _errorHandler?
Thx!
Upvotes: 3
Views: 7426
Reputation: 13396
Catch Blocks in Services & Subscription Error Callbacks in Components
This is what I have come to use as my project has developed. The catch block in the service is responsible for transforming the error. The error callback in the component is responsible for updating any view logic.
Service
I rarely get back the data I need in the format I need from the api, so I transform the data within a .map() method. Then I attach a catch block to the sequence.
// Service Class
getTableData(): Observable<Table>{
return this.http.get('url')
.map(res => {
// transform data as needed
return transformedData;
}
.catch(err => {
// transform error as needed
let transformedError = GlobalFunction.transformError(err);
return Observable.throw(transformedError);
}
}
now I perform whatever transform I want when an error occurs. For example, I may call a global method that converts an error into a user friendly response
// GlobalFunction Class
transformError(err){
switch(err.status){
case 404:
return {
status: err.status,
message: 'Oops, it looks like that resource was not found'
}
break;
}
}
Component
Now in my component, I subscribe to the sequence
// Component Class
ngOnInit(){
this.loading = true;
this.error = false;
this.subscription = this.service.getTableData().subscribe(
res => {
this.table = res;
this.loading = false;
},
err => {
this.message = err.message;
this.error = true;
this.loading = false;
}
);
}
By containing all of my data transform in the service, I have kept the view logic nice & lean. I believe this approach keeps the pieces separate. The service provides the data & the component updates the view.
Upvotes: 11
Reputation: 9331
Catch only in Components
This is what I do in my project and it worked very well. Services never have catch block. Every method returns a stream and all params are streams. The catch block is put inside component and appropriate action is taken.
Exceptions
There are some exceptions where I catch in between .
It is very rare I handle the errors in between. Most of the time the stream starts and ends in the component and error handling is done in the components.
Example
All params ending with $
are streams.
Component:
public error = false;
public errorMessage = '';
public users$;
constructor(private router: Router, private _userService: UserService){
const userId$ = this.router.params.map(params => params['id']);
this.users$ = this._userService.getUsers$(userId$)
.catch(error => {
this.error = true;
//translate error into user friendly language
this.errorMessage = error;
});
}
HTML
<ul *ngIf="!error">
<li *ngFor="let user of users$ | async " >{{user.name}}</li>
</ul>
UserService
public getUsers$(userId$: Observable<string>): Observable<User[]>{
return userId$.flatMap(
userId => this.authService.getAuthToken$()
.flatMap(
authToken => this.http.get('url', {userId})
.map(res => res.json())
);
}
AuthService will have a catch block which redirects the user to login page if authtoken is not found or session expired or any reason. Rest all places for all errors the catch block is at the component.
I just came up with this example, if there are any errors let me know .
Upvotes: 6