Angular 2 - I have many pages in my application and where in each page I am calling the backend services. Here my query was how to create a common error-popup component where it will take error message and show the popup when ever it required my application. Can you please help to find the solution on this. Thank you.
-
1angular.io/guide/http#intercepting-requests-and-responsesJB Nizet– JB Nizet2018-03-03 08:04:22 +00:00Commented Mar 3, 2018 at 8:04
-
This is too broad. You need to do some research, try something, and focus on one of the concrete problems you're facing.JB Nizet– JB Nizet2018-03-03 08:05:25 +00:00Commented Mar 3, 2018 at 8:05
2 Answers
This is how I did it in Angular 5, you can do it similar in Angular2, since it is not that different. I hope this will help you.
In my app.component.html, I added this:
<div class="mesaages">
<div *ngIf="errorMessage" class="alert alert-danger alert-dismissible fade show" role="alert">
<button type="button" class="close" (click)="errorMessage=''">
<span aria-hidden="true">×</span>
</button>
<i class="fa fa-exclamation-circle fa-1x" aria-hidden="true"></i> {{ errorMessage}}
</div>
<div *ngIf="busyinfoMessage" class="alert alert-success alert-dismissible fade show" role="alert">
<button type="button" class="close" (click)="busyinfoMessage=''">
<span aria-hidden="true">×</span>
</button>
<i class="fa fa-spinner fa-pulse fa-1x fa-fw" aria-hidden="true" placement="right"></i> {{ busyinfoMessage}}
</div>
<div *ngIf="infoMessage" class="alert alert-info alert-dismissible fade show" (mouseover)="stopTimeout('w')" (mouseout)="_actionService.infoMessage(infoMessage,3)" role="alert">
<button type="button" class="close" (click)="infoMessage=''">
<span aria-hidden="true">×</span>
</button>
<i class="fa fa-info-circle fa-1x" aria-hidden="true" placement="right"></i> {{ infoMessage}}
</div>
<div *ngIf="warningMessage" class="alert alert-warning alert-dismissible fade show" (mouseover)="stopTimeout('w')" (mouseout)="_actionService.warningMessage(warningMessage,3)" role="alert">
<button type="button" class="close" (click)="warningMessage=''">
<span aria-hidden="true">×</span>
</button>
<i class="fa fa-bell fa-1x" aria-hidden="true"></i> {{ warningMessage}}
</div>
in my CSS file I added this:
.messages {
float: left;
top: 10px;
right: 10px;
width: 100;
height: 20;
padding: 6px;
position: fixed;
border-radius: 8px;
z-index: 9999;
}
This css code will place this toast like message in right upper corner of page.
Then I cretated action.service.ts which is used for showing this messages and to toggle my loader-spinner:
import { Injectable, EventEmitter } from '@angular/core';
@Injectable()
export class ActionService {
public autoloader = true;
public _toggleLoader: EventEmitter<any>;
public _errorMessage: EventEmitter<any>;
public _busyinfoMessage: EventEmitter<any>;
public _warningMessage: EventEmitter<any>;
public _infoMessage: EventEmitter<any>;
public timeoutInfo: any;
public timeoutWarning: any;
constructor() {
this._toggleLoader = new EventEmitter<any>();
this._errorMessage = new EventEmitter<any>();
this._busyinfoMessage = new EventEmitter<any>();
this._warningMessage = new EventEmitter<any>();
this._infoMessage = new EventEmitter<any>();
}
toggleLoader(toggle: boolean) {
this._toggleLoader.emit(toggle);
}
errorMessage(errorMessage: string) {
this._errorMessage.emit(errorMessage);
}
busyinfoMessage(busyinfoMessage: string) {
this._busyinfoMessage.emit(busyinfoMessage);
}
warningMessage(warningMessage: string, timeout: number) {
if (timeout === 0) {
this._warningMessage.emit(warningMessage);
return;
}
this.timeoutWarning = setTimeout(() => {
this._warningMessage.emit('');
}, 1000 * timeout);
this._warningMessage.emit(warningMessage);
}
infoMessage(infoMessage: string, timeout: number) {
if (timeout === 0) {
this._infoMessage.emit(infoMessage);
return;
}
this.timeoutInfo = setTimeout(() => {
this._infoMessage.emit('');
}, 1000 * timeout);
this._infoMessage.emit(infoMessage);
}
stopTimeout(tip: string) {
if (tip === 'w') {
clearTimeout(this.timeoutWarning);
} else {
clearTimeout(this.timeoutInfo);
}
}
}
}
Im my app.component.ts file I added this lines of code:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { ActionService } from './action.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit, OnDestroy {
private ngUnsubscribe: Subject<any> = new Subject();
public loader = false;
public errorMessage = '';
public warningMessage = '';
public busyinfoMessage = '';
public infoMessage = '';
constructor(
private _router: Router,
private _actionService: ActionService
) {
}
ngOnInit() {
this._actionService._toggleLoader
.takeUntil(this.ngUnsubscribe)
.subscribe(res => {
setTimeout(() => this.loader = res, 0);
});
this._actionService._errorMessage
.takeUntil(this.ngUnsubscribe)
.subscribe(res => {
this.errorMessage = res;
});
this._actionService._warningMessage
.takeUntil(this.ngUnsubscribe)
.subscribe(res => {
this.warningMessage = res;
});
this._actionService._busyinfoMessage
.takeUntil(this.ngUnsubscribe)
.subscribe(res => {
this.busyinfoMessage = res;
});
this._actionService._infoMessage
.takeUntil(this.ngUnsubscribe)
.subscribe(res => {
this.infoMessage = res;
});
}
stopTimeout(tip: string) {
this._actionService.stopTimeout(tip);
}
ngOnDestroy() {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
}
Since I'm using Angular 5 I created this error interceptor for showing error messages after every call:
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import { ActionService } from './action.service';
import { Router } from '@angular/router';
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
constructor(
private _actionService: ActionService,
private _router: Router) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request)
.catch((err: HttpErrorResponse, caught) => {
if (err.status === 0) {
this._actionService.errorMessage('Check you connection to interent!');
return Observable.empty<HttpEvent<any>>();
}
if (err.status === 500) {
this._actionService.errorMessage('Server error : ' + err.error.ExceptionMessage);
return Observable.empty<HttpEvent<any>>();
}
if (err.status === 401 || err.status === 403) {
this._router.navigateByUrl('prijava');
return Observable.empty<HttpEvent<any>>();
}
this._actionService.errorMessage('Unknown error. Error message: ' + (err.error.ExceptionMessage || err.error.Message));
return Observable.empty<HttpEvent<any>>();
})
}
}
export const ErrorInterceptorProvider = {
provide: HTTP_INTERCEPTORS,
useClass: ErrorInterceptor,
multi: true
};
Since you are using Angular 2, my suggestion to you would be to create HTTP.Service and to implemet calls for errors in that service. Something like this:
import { Injectable, ReflectiveInjector } from '@angular/core';
import { Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers } from '@angular/http';
import { LocatorService } from './locator.service';
import { Observable } from 'rxjs/Observable';
import { Router } from '@angular/router';
import { ActionService } from './action.service';
import { Url } from './global';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
@Injectable()
export class HttpService extends Http {
private currentRequests: number;
private _router: Router;
private _actionService: ActionService;
constructor(backend: XHRBackend, options: RequestOptions, router: Router) {
let token = localStorage.getItem('token'); // your custom token getter function here
options.url = Url + (options.url == null ? '' : options.url);
options.headers.set('Authorization', `Bearer ${token}`);
super(backend, options);
this._actionService = LocatorService.injector.get(ActionService);
this._router = router;
this.currentRequests = 0;
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
let token = localStorage.getItem('token');
if (typeof url === 'string') { // meaning we have to add the token to the options, not in url
if (!options) {
// let's make option object
options = { headers: new Headers() };
}
options.headers.set('Authorization', `Bearer ${localStorage.getItem('token')}`);
options.url = Url + url;
} else {
// we have to add the token to the url object
if (!options) {
// let's make option object
options = { headers: new Headers() };
}
options.headers.set('Authorization', `Bearer ${localStorage.getItem('token')}`);
url.url = Url + url.url;
}
if (this._actionService.autoloader) { this._actionService.toggleLoader(true); }
return super.request(url, options)
.catch(
this.catchError(this)
)
.finally(() => {
setTimeout(() => {
if (this._actionService.autoloader) { this._actionService.toggleLoader(false); }
}, 100);
}
);
}
private catchError(self: HttpService) {
// we have to pass HttpService's own instance here as `self`
return (res: Response) => {
console.log(res);
switch (res.status) {
case 0:
this._actionService.errorMessage('Check your internet connection!');
return Observable.throw(null);
case 500:
this._actionService.errorMessage('Server error : ' + res.json()['ExceptionMessage']);
return Observable.throw(null);
case 401:
case 403:
this._router.navigateByUrl('prijava');
return Observable.throw(null);
default:
this._actionService.errorMessage('Unknown error. Error message: ' + (res.json()['ExceptionMessage'] || res.json()['Message']));
return Observable.throw(null);
}
};
}
}
I hope that is all. If you need any clarification please ask. I hope this will help you.
Comments
For user notification on errors, i use Angular Material. On running an http query. I handle errors from http observables using the error=> arrow function in the Observable result. Forexample this is an auth login query to my restful API.
...
constructor(private _dialogService: TdDialogService,
private _viewContainerRef: ViewContainerRef,
private authenticationService: AuthenticationService){}
login() {
this.authenticationService.login(this.formLogin.value.email, this.formLogin.value.password)
.subscribe(
data => {
// Handle any 200 result
},
error => {
// Handle any http errors, 401 UnAuthorized or 500 or any other
this.openAlert('Invalid username or password'); // You can pass in your error message
});
}
openAlert(message: string) {
this._dialogService.openAlert({
message,
disableClose: true,
viewContainerRef: this._viewContainerRef,
title: 'Error',
closeButton: 'Ok'
});
}
}
Result
