1

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.

2

2 Answers 2

1

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">&times;</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">&times;</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">&times;</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">&times;</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.

Sign up to request clarification or add additional context in comments.

Comments

0

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

enter image description here

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.