├── .angulardoc.json
├── .gitignore
├── README.md
├── angular.json
├── package-lock.json
├── package.json
├── src
├── app
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── global-error-handler.ts
│ ├── server-error.interceptor.ts
│ └── services
│ │ ├── error.service.ts
│ │ ├── logging.service.ts
│ │ └── notification.service.ts
├── assets
│ └── .gitkeep
├── browserslist
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── styles.css
└── tsconfig.app.json
└── tsconfig.json
/.angulardoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "repoId": "1bf8ceb1-a27a-45be-a2cc-def4207ddb9e",
3 | "lastSync": 0
4 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 |
11 | # profiling files
12 | chrome-profiler-events.json
13 | speed-measure-plugin.json
14 |
15 | # IDEs and editors
16 | /.idea
17 | .project
18 | .classpath
19 | .c9/
20 | *.launch
21 | .settings/
22 | *.sublime-workspace
23 |
24 | # IDE - VSCode
25 | .vscode/*
26 | !.vscode/settings.json
27 | !.vscode/tasks.json
28 | !.vscode/launch.json
29 | !.vscode/extensions.json
30 | .history/*
31 |
32 | # misc
33 | /.sass-cache
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | npm-debug.log
38 | yarn-error.log
39 | testem.log
40 | /typings
41 |
42 | # System Files
43 | .DS_Store
44 | Thumbs.db
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GlobalErrorHandling
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.2.1.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
28 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "global-error-handling": {
7 | "root": "",
8 | "sourceRoot": "src",
9 | "projectType": "application",
10 | "prefix": "app",
11 | "schematics": {
12 | "@schematics/angular:class": {
13 | "skipTests": true
14 | },
15 | "@schematics/angular:component": {
16 | "skipTests": true
17 | },
18 | "@schematics/angular:directive": {
19 | "skipTests": true
20 | },
21 | "@schematics/angular:guard": {
22 | "skipTests": true
23 | },
24 | "@schematics/angular:module": {
25 | "skipTests": true
26 | },
27 | "@schematics/angular:pipe": {
28 | "skipTests": true
29 | },
30 | "@schematics/angular:service": {
31 | "skipTests": true
32 | }
33 | },
34 | "architect": {
35 | "build": {
36 | "builder": "@angular-devkit/build-angular:browser",
37 | "options": {
38 | "outputPath": "dist/global-error-handling",
39 | "index": "src/index.html",
40 | "main": "src/main.ts",
41 | "polyfills": "src/polyfills.ts",
42 | "tsConfig": "src/tsconfig.app.json",
43 | "assets": [
44 | "src/favicon.ico",
45 | "src/assets"
46 | ],
47 | "styles": [
48 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
49 | "src/styles.css"
50 | ],
51 | "scripts": []
52 | },
53 | "configurations": {
54 | "production": {
55 | "fileReplacements": [
56 | {
57 | "replace": "src/environments/environment.ts",
58 | "with": "src/environments/environment.prod.ts"
59 | }
60 | ],
61 | "optimization": true,
62 | "outputHashing": "all",
63 | "sourceMap": false,
64 | "extractCss": true,
65 | "namedChunks": false,
66 | "aot": true,
67 | "extractLicenses": true,
68 | "vendorChunk": false,
69 | "buildOptimizer": true,
70 | "budgets": [
71 | {
72 | "type": "initial",
73 | "maximumWarning": "2mb",
74 | "maximumError": "5mb"
75 | }
76 | ]
77 | }
78 | }
79 | },
80 | "serve": {
81 | "builder": "@angular-devkit/build-angular:dev-server",
82 | "options": {
83 | "browserTarget": "global-error-handling:build"
84 | },
85 | "configurations": {
86 | "production": {
87 | "browserTarget": "global-error-handling:build:production"
88 | }
89 | }
90 | },
91 | "extract-i18n": {
92 | "builder": "@angular-devkit/build-angular:extract-i18n",
93 | "options": {
94 | "browserTarget": "global-error-handling:build"
95 | }
96 | },
97 | "test": {
98 | "builder": "@angular-devkit/build-angular:karma",
99 | "options": {
100 | "main": "src/test.ts",
101 | "polyfills": "src/polyfills.ts",
102 | "tsConfig": "src/tsconfig.spec.json",
103 | "karmaConfig": "src/karma.conf.js",
104 | "styles": [
105 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
106 | "src/styles.css"
107 | ],
108 | "scripts": [],
109 | "assets": [
110 | "src/favicon.ico",
111 | "src/assets"
112 | ]
113 | }
114 | },
115 | "lint": {
116 | "builder": "@angular-devkit/build-angular:tslint",
117 | "options": {
118 | "tsConfig": [
119 | "src/tsconfig.app.json",
120 | "src/tsconfig.spec.json"
121 | ],
122 | "exclude": [
123 | "**/node_modules/**"
124 | ]
125 | }
126 | }
127 | }
128 | }
129 | },
130 | "defaultProject": "global-error-handling"
131 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "global-error-handling",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "test": "ng test",
9 | "lint": "ng lint",
10 | "e2e": "ng e2e"
11 | },
12 | "private": true,
13 | "dependencies": {
14 | "@angular/animations": "7.2.2",
15 | "@angular/cdk": "7.2.2",
16 | "@angular/common": "7.2.2",
17 | "@angular/compiler": "7.2.2",
18 | "@angular/core": "7.2.2",
19 | "@angular/forms": "7.2.2",
20 | "@angular/material": "^7.2.2",
21 | "@angular/platform-browser": "7.2.2",
22 | "@angular/platform-browser-dynamic": "7.2.2",
23 | "@angular/router": "7.2.2",
24 | "@types/node": "8.9.5",
25 | "core-js": "2.6.3",
26 | "rxjs": "6.3.3",
27 | "tslib": "1.9.3",
28 | "zone.js": "0.8.29"
29 | },
30 | "devDependencies": {
31 | "@angular-devkit/build-angular": "0.12.3",
32 | "@angular/cli": "7.2.3",
33 | "@angular/compiler-cli": "7.2.2",
34 | "@angular/language-service": "7.2.2",
35 | "ts-node": "~7.0.0",
36 | "tslint": "~5.11.0",
37 | "typescript": "~3.2.2"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
3 |
4 | @Component({
5 | selector: 'app-root',
6 | template: `
7 |
8 |
9 |
10 |
11 | `
12 | })
13 | export class AppComponent {
14 |
15 | constructor(private http: HttpClient) {
16 | }
17 |
18 | throwError(){
19 | throw new Error('My Pretty Error');
20 | }
21 |
22 | throwHttpError(){
23 | this.http.get('urlhere').subscribe();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { GlobalErrorHandler } from './global-error-handler';
2 | import { BrowserModule } from '@angular/platform-browser';
3 | import { NgModule, ErrorHandler } from '@angular/core';
4 |
5 | import { AppComponent } from './app.component';
6 | import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
7 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
8 | import { MatSnackBarModule } from '@angular/material/snack-bar';
9 | import { ServerErrorInterceptor } from './server-error.interceptor';
10 |
11 | @NgModule({
12 | declarations: [
13 | AppComponent
14 | ],
15 | imports: [
16 | BrowserModule,
17 | HttpClientModule,
18 | BrowserAnimationsModule,
19 | MatSnackBarModule
20 | ],
21 | providers: [
22 | { provide: ErrorHandler, useClass: GlobalErrorHandler },
23 | { provide: HTTP_INTERCEPTORS, useClass: ServerErrorInterceptor, multi: true }
24 | ],
25 | bootstrap: [AppComponent]
26 | })
27 | export class AppModule { }
28 |
--------------------------------------------------------------------------------
/src/app/global-error-handler.ts:
--------------------------------------------------------------------------------
1 | import { ErrorHandler, Injectable, Injector } from '@angular/core';
2 | import { HttpErrorResponse } from '@angular/common/http';
3 | import { LoggingService } from './services/logging.service';
4 | import { ErrorService } from './services/error.service';
5 | import { NotificationService } from './services/notification.service';
6 |
7 | @Injectable()
8 | export class GlobalErrorHandler implements ErrorHandler {
9 |
10 | constructor(private injector: Injector) { }
11 |
12 | handleError(error: Error | HttpErrorResponse) {
13 | const errorService = this.injector.get(ErrorService);
14 | const logger = this.injector.get(LoggingService);
15 | const notifier = this.injector.get(NotificationService);
16 |
17 | let message;
18 | let stackTrace;
19 | if (error instanceof HttpErrorResponse) {
20 | // Server error
21 | message = errorService.getServerErrorMessage(error);
22 | //stackTrace = errorService.getServerErrorStackTrace(error);
23 | notifier.showError(message);
24 | } else {
25 | // Client Error
26 | message = errorService.getClientErrorMessage(error);
27 | notifier.showError(message);
28 | }
29 | // Always log errors
30 | logger.logError(message, stackTrace);
31 | console.error(error);
32 | }
33 | }
--------------------------------------------------------------------------------
/src/app/server-error.interceptor.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import {
3 | HttpEvent, HttpRequest, HttpHandler,
4 | HttpInterceptor, HttpErrorResponse
5 | } from '@angular/common/http';
6 | import { Observable, throwError } from 'rxjs';
7 | import { retry, catchError } from 'rxjs/operators';
8 |
9 | @Injectable()
10 | export class ServerErrorInterceptor implements HttpInterceptor {
11 |
12 | intercept(request: HttpRequest, next: HttpHandler): Observable> {
13 |
14 | return next.handle(request).pipe(
15 | retry(1),
16 | catchError((error: HttpErrorResponse) => {
17 | if (error.status === 401) {
18 | // refresh token
19 | } else {
20 | return throwError(error);
21 | }
22 | })
23 | );
24 | }
25 | }
--------------------------------------------------------------------------------
/src/app/services/error.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { HttpErrorResponse } from '@angular/common/http';
3 |
4 | @Injectable({
5 | providedIn: 'root'
6 | })
7 | export class ErrorService {
8 |
9 | getClientErrorMessage(error: Error): string {
10 | return error.message ?
11 | error.message :
12 | error.toString();
13 | }
14 |
15 | getServerErrorMessage(error: HttpErrorResponse): string {
16 | return navigator.onLine ?
17 | error.message :
18 | 'No Internet Connection';
19 | }
20 | }
--------------------------------------------------------------------------------
/src/app/services/logging.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | @Injectable({
4 | providedIn: 'root'
5 | })
6 | export class LoggingService {
7 |
8 | logError(message: string, stack: string) {
9 | // Send errors to server here
10 | console.log('LoggingService: ' + message);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/services/notification.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, NgZone } from '@angular/core';
2 | import { MatSnackBar } from '@angular/material/snack-bar';
3 |
4 | @Injectable({
5 | providedIn: 'root'
6 | })
7 | export class NotificationService {
8 |
9 | constructor(
10 | public snackBar: MatSnackBar,
11 | private zone: NgZone) { }
12 |
13 | showSuccess(message: string): void {
14 | // Had an issue with the snackbar being ran outside of angular's zone.
15 | this.zone.run(() => {
16 | this.snackBar.open(message);
17 | });
18 | }
19 |
20 | showError(message: string): void {
21 | this.zone.run(() => {
22 | // The second parameter is the text in the button.
23 | // In the third, we send in the css class for the snack bar.
24 | this.snackBar.open(message, 'X', {panelClass: ['error']});
25 | });
26 | }
27 | }
--------------------------------------------------------------------------------
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/melcor76/global-error-handling/7325aedade93f8c8c7a440881e08c61ca3c0d829/src/assets/.gitkeep
--------------------------------------------------------------------------------
/src/browserslist:
--------------------------------------------------------------------------------
1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 | #
5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
6 |
7 | > 0.5%
8 | last 2 versions
9 | Firefox ESR
10 | not dead
11 | not IE 9-11
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/melcor76/global-error-handling/7325aedade93f8c8c7a440881e08c61ca3c0d829/src/favicon.ico
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | GlobalErrorHandling
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.error(err));
13 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10, IE11, and Chrome <55 requires all of the following polyfills.
22 | * This also includes Android Emulators with older versions of Chrome and Google Search/Googlebot
23 | */
24 |
25 | // import 'core-js/es6/symbol';
26 | // import 'core-js/es6/object';
27 | // import 'core-js/es6/function';
28 | // import 'core-js/es6/parse-int';
29 | // import 'core-js/es6/parse-float';
30 | // import 'core-js/es6/number';
31 | // import 'core-js/es6/math';
32 | // import 'core-js/es6/string';
33 | // import 'core-js/es6/date';
34 | // import 'core-js/es6/array';
35 | // import 'core-js/es6/regexp';
36 | // import 'core-js/es6/map';
37 | // import 'core-js/es6/weak-map';
38 | // import 'core-js/es6/set';
39 |
40 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
41 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
42 |
43 | /** IE10 and IE11 requires the following for the Reflect API. */
44 | // import 'core-js/es6/reflect';
45 |
46 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
47 | import 'core-js/es7/reflect';
48 |
49 | /**
50 | * Web Animations `@angular/platform-browser/animations`
51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
53 | */
54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
55 |
56 | /**
57 | * By default, zone.js will patch all possible macroTask and DomEvents
58 | * user can disable parts of macroTask/DomEvents patch by setting following flags
59 | * because those flags need to be set before `zone.js` being loaded, and webpack
60 | * will put import in the top of bundle, so user need to create a separate file
61 | * in this directory (for example: zone-flags.ts), and put the following flags
62 | * into that file, and then add the following code before importing zone.js.
63 | * import './zone-flags.ts';
64 | *
65 | * The flags allowed in zone-flags.ts are listed here.
66 | *
67 | * The following flags will work for all browsers.
68 | *
69 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
70 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
71 | * (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
72 | *
73 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
74 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
75 | *
76 | * (window as any).__Zone_enable_cross_context_check = true;
77 | *
78 | */
79 |
80 | /***************************************************************************************************
81 | * Zone JS is required by default for Angular itself.
82 | */
83 | import 'zone.js/dist/zone'; // Included with Angular CLI.
84 |
85 |
86 | /***************************************************************************************************
87 | * APPLICATION IMPORTS
88 | */
89 |
--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
3 | html, body { height: 100%; }
4 | body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
5 | .error {
6 | background-color: red;
7 | color: white;
8 | }
9 |
10 | .error button {
11 | background-color:orangered;
12 | color: white;
13 | }
14 |
--------------------------------------------------------------------------------
/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "types": []
6 | },
7 | "exclude": [
8 | "test.ts",
9 | "**/*.spec.ts"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "module": "es2015",
9 | "moduleResolution": "node",
10 | "emitDecoratorMetadata": true,
11 | "experimentalDecorators": true,
12 | "importHelpers": true,
13 | "target": "es5",
14 | "typeRoots": [
15 | "node_modules/@types"
16 | ],
17 | "lib": [
18 | "es2018",
19 | "dom"
20 | ]
21 | }
22 | }
23 |
--------------------------------------------------------------------------------