+ `
+})
+// #docregion class
+export class AppComponent {
+ constructor() {}
+
+ throwError(): void {
+ throw(new Error('No Disassemble!'));
+ }
+}
+// #enddocregion class
+// #enddocregion component
diff --git a/public/docs/_examples/cb-exception-handler/ts/app/app.module.ts b/public/docs/_examples/cb-exception-handler/ts/app/app.module.ts
new file mode 100644
index 0000000000..5690dbfd6f
--- /dev/null
+++ b/public/docs/_examples/cb-exception-handler/ts/app/app.module.ts
@@ -0,0 +1,28 @@
+// #docregion
+import { BrowserModule } from '@angular/platform-browser';
+import { ExceptionHandler } from '@angular/core';
+import { NgModule } from '@angular/core';
+
+import { AppComponent } from './app.component';
+import { CustomExceptionHandler } from './custom-exception-handler';
+import { ErrorLoggingService } from './error-logging.service';
+
+// #docregion module
+@NgModule({
+ imports: [ BrowserModule ],
+ providers: [
+ ErrorLoggingService,
+
+ // In order to override the default ExceptionHandler service, we have to provide our
+ // custom implementation as part of the application module - if we try to do this
+ // later on, in the App component, it will be too late.
+ {
+ provide: ExceptionHandler,
+ useClass: CustomExceptionHandler
+ }
+ ],
+ declarations: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
+// #enddocregion module
diff --git a/public/docs/_examples/cb-exception-handler/ts/app/custom-exception-handler.ts b/public/docs/_examples/cb-exception-handler/ts/app/custom-exception-handler.ts
new file mode 100644
index 0000000000..8163771455
--- /dev/null
+++ b/public/docs/_examples/cb-exception-handler/ts/app/custom-exception-handler.ts
@@ -0,0 +1,46 @@
+// #docregion
+import { ExceptionHandler, Injectable } from '@angular/core';
+
+import { ErrorLoggingService } from './error-logging.service';
+
+// #docregion class
+@Injectable()
+export class CustomExceptionHandler {
+ constructor(private errorLogggingService: ErrorLoggingService) {}
+
+ call(exception: any, stackTrace?: any, reason?: string): void {
+ this.logToConsole(exception, stackTrace, reason);
+
+ try {
+ // Internally, Angular wraps Error objects inside a proxy; therefore, when we
+ // send the error to the logging service, we want to try and unwrap the error
+ // first so that we're more likely to send-over a native Error object.
+ // --
+ // CAUTION: This is NOT A DOCUMENTED BEHAVIOR - you have to look at the Angular
+ // source code in order to see that this happens.
+ this.errorLogggingService.log(this.unwrapError(exception));
+ } catch (loggingError) {
+ this.logToConsole(loggingError);
+ }
+ }
+
+ private logToConsole(exception: any, stackTrace?: any, reason?: string): void {
+ // Even though we are replacing the core _instance_ of the ExceptionHandler, we can
+ // still leverage the core class' static method for stringification of the error.
+ let stringified = ExceptionHandler.exceptionToString(exception, stackTrace, reason);
+
+ if (console && console.group && console.log) {
+ console.group('Custom Exception Handler');
+ console.log(stringified);
+ console.groupEnd();
+ }
+ }
+
+ private unwrapError(exception: any): any {
+ while (exception.originalException) {
+ exception = exception.originalException;
+ }
+ return exception;
+ }
+}
+// #enddocregion class
diff --git a/public/docs/_examples/cb-exception-handler/ts/app/error-logging.service.ts b/public/docs/_examples/cb-exception-handler/ts/app/error-logging.service.ts
new file mode 100644
index 0000000000..656ced21ef
--- /dev/null
+++ b/public/docs/_examples/cb-exception-handler/ts/app/error-logging.service.ts
@@ -0,0 +1,48 @@
+// #docplaster
+// #docregion
+import { Injectable } from '@angular/core';
+
+// #docregion class
+@Injectable()
+export class ErrorLoggingService {
+ log(error: any) {
+ // Internal tracking.
+ this.sendToConsole(error);
+ this.sendToServer(error);
+
+ // Software-as-a-Service (SaaS) tracking.
+ this.sendToNewRelic(error);
+ this.sendToRaygun(error);
+ this.sendToRollbar(error);
+ this.sendToTrackJs(error);
+ }
+
+ private sendToConsole(error: any): void {
+ if (console && console.group && console.error) {
+ console.group('Error Logging Service');
+ console.error(error);
+ console.groupEnd();
+ }
+ }
+
+ private sendToRollbar(error: any): void {
+ // Example: Rollbar.error(error);
+ }
+
+ private sendToNewRelic(error: any): void {
+ // Example: newrelic.noticeError(error);
+ }
+
+ private sendToRaygun(error: any): void {
+ // Example: Raygun.send(error);
+ }
+
+ private sendToServer(error: any): void {
+ // ... use http service to send error to your own server.
+ }
+
+ private sendToTrackJs(error: any): void {
+ // Example: trackJs.track(error);
+ }
+}
+// #enddocregion class
diff --git a/public/docs/_examples/cb-exception-handler/ts/app/main.ts b/public/docs/_examples/cb-exception-handler/ts/app/main.ts
new file mode 100644
index 0000000000..3b8a440cbd
--- /dev/null
+++ b/public/docs/_examples/cb-exception-handler/ts/app/main.ts
@@ -0,0 +1,7 @@
+/* tslint:disable */
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/cb-exception-handler/ts/example-config.json b/public/docs/_examples/cb-exception-handler/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/cb-exception-handler/ts/index.html b/public/docs/_examples/cb-exception-handler/ts/index.html
new file mode 100644
index 0000000000..352a3b2bb9
--- /dev/null
+++ b/public/docs/_examples/cb-exception-handler/ts/index.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+ Custom Exception Handler Service
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Custom Exception Handler Service
+
+
+ Loading app...
+
+
+
diff --git a/public/docs/_examples/cb-exception-handler/ts/plnkr.json b/public/docs/_examples/cb-exception-handler/ts/plnkr.json
new file mode 100644
index 0000000000..5c50eef870
--- /dev/null
+++ b/public/docs/_examples/cb-exception-handler/ts/plnkr.json
@@ -0,0 +1,9 @@
+{
+ "description": "Custom Exception Handler In Angular 2",
+ "files": [
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.[1].*"
+ ],
+ "tags": [ "cookbook" ]
+}
diff --git a/public/docs/_examples/cb-exception-handler/ts/sample.css b/public/docs/_examples/cb-exception-handler/ts/sample.css
new file mode 100644
index 0000000000..d99cc43fbd
--- /dev/null
+++ b/public/docs/_examples/cb-exception-handler/ts/sample.css
@@ -0,0 +1,5 @@
+a {
+ color: #369 ;
+ cursor: pointer ;
+ text-decoration: underline ;
+}
diff --git a/public/docs/ts/latest/cookbook/_data.json b/public/docs/ts/latest/cookbook/_data.json
index f82d816cef..c2658c9ced 100644
--- a/public/docs/ts/latest/cookbook/_data.json
+++ b/public/docs/ts/latest/cookbook/_data.json
@@ -26,6 +26,11 @@
"intro": "Use relative URLs for component templates and styles."
},
+ "custom-exception-handler": {
+ "title": "Custom Exception Handler",
+ "intro": "Creating a custom exception handler service."
+ },
+
"dependency-injection": {
"title": "Dependency Injection",
"intro": "Techniques for Dependency Injection"
diff --git a/public/docs/ts/latest/cookbook/custom-exception-handler.jade b/public/docs/ts/latest/cookbook/custom-exception-handler.jade
new file mode 100644
index 0000000000..88b0bca75b
--- /dev/null
+++ b/public/docs/ts/latest/cookbook/custom-exception-handler.jade
@@ -0,0 +1,90 @@
+include ../_util-fns
+
+a(id='top')
+:marked
+ When Angular executes the vast majority of our application code, it does so within the
+ context of a `try-catch` block. And, when it catches errors, it sends them to the
+ default `ExceptionHandler` which, in turn, logs them to the browser console. This is
+ great for debugging in our development environment; but, in a production setting, it
+ would be helpful to track these errors more explicitly. To do this, we can implement a
+ custom exception handler class.
+
+:marked
+ **See the [live example](/resources/live-examples/cb-exception-handler/ts/plnkr.html)**.
+
+:marked
+ First though, let's think about what we might want to do with errors in a production
+ environment. Perhaps we want to `POST` them, over HTTP, to our server for logging. Or,
+ maybe we're using a client-side error-tracking service like New Relic, TrackJS, or
+ Raygun. We can create an `ErrorLoggingService` class that is responsible for routing
+ errors to different destinations:
+
++makeExample('cb-exception-handler/ts/app/error-logging.service.ts', 'class', 'app/error-logging.service.ts')(format='.')
+
+:marked
+ Once we have this error-logging class, we can start piping errors into it through the
+ use of a custom exception handler. Since the entire Angular platform depends on proper
+ exception handling, we have to override the `ExceptionHandler` in the providers of our
+ **application module**. To do this, we have to associate our custom exception handler
+ class — `CustomExceptionHandler` — with the core `ExceptionHandler`
+ dependency-injection (DI) token. This way, when the Angular internals request an
+ instance of `ExceptionHandler`, they will be provided with an instance of our
+ `CustomExceptionHandler`.
+
++makeExample('cb-exception-handler/ts/app/app.module.ts', '', 'app/app.module.ts (root module for bootstrapping)')(format='.')
+
+:marked
+ The default `ExceptionHandler` service only has one public method, `.call()`. As such,
+ our custom exception handler only has to implement the `.call()` method. Inside this
+ `.call()` method, we're going to log the errors to the console and send them over to
+ the `ErrorLoggingService` instance.
+
++makeExample('cb-exception-handler/ts/app/custom-exception-handler.ts', 'class', 'app/custom-exception-handler.ts')(format='.')
+
+:marked
+ When Angular passes an error into the application exception handler, it doesn't
+ necessarily pass-in the raw error object — it may pass-in a wrapped error. This
+ is _not a documented behavior_; but, if we look at the source code for the core
+ `ExceptionHandler` class, we can see that it does this. As such, in our custom
+ exception handler, we attempt to unwrap the root error — traversing the
+ `.originalException` object path — before sending it to the
+ `ErrorLoggingService`.
+
+:marked
+ At this point, we've overridden the core `ExceptionHandler` and laid the ground-work
+ for powerful error logging. Now, we just need to `throw()` an error in our application
+ and confirm that it's being seen by both the `CustomExceptionHandler` and the
+ `ErrorLoggingService` classes
+
++makeExample('cb-exception-handler/ts/app/app.component.ts', 'component', 'app/app.component.ts')(format='.')
+
+:marked
+ When we run this application and trigger an error, we can see it getting logged to the
+ console by both the custom exception handler and our error-logging service:
+
+figure.image-display
+ img(src='/resources/images/cookbooks/custom-exception-handler/custom-exception-handler-animation.gif' alt='Custom exception handler in Angular2.')
+
+:marked
+ Here's the complete solution:
+
++makeTabs(
+ `
+ cb-exception-handler/ts/app/main.ts,
+ cb-exception-handler/ts/app/app.module.ts,
+ cb-exception-handler/ts/app/custom-exception-handler.ts,
+ cb-exception-handler/ts/app/error-logging.service.ts,
+ cb-exception-handler/ts/app/app.component.ts
+ `,
+ '',
+ `
+ main.ts,
+ app.module.ts,
+ custom-exception-handler.ts,
+ error-logging.service.ts,
+ app.component.ts
+ `
+)
+
+:marked
+ [Back to top](#top)
diff --git a/public/resources/images/cookbooks/custom-exception-handler/custom-exception-handler-animation.gif b/public/resources/images/cookbooks/custom-exception-handler/custom-exception-handler-animation.gif
new file mode 100644
index 0000000000..80275ca888
Binary files /dev/null and b/public/resources/images/cookbooks/custom-exception-handler/custom-exception-handler-animation.gif differ