Angularでエラーハンドリングする2つの方法

# エラーハンドリングしよう！
## はじめに
クライアント側(ブラウザ)で発生したエラー情報をWebサーバ側に渡してログ書いたり画面遷移させたり何らかの処理をさせたいときに、どうやったらエラー情報を取得できるか、またどうやったら独自処理を実装できるのか調べました。

※TypeScriptで実装しています。

## $exceptionHandler

AngularJSでは例外が投げられた場合、`$exceptionHandler`というサービスに処理が委譲され、`$log`サービスでブラウザコンソールにログが出力されます(※1)。

エラー処理を独自で実装したい場合の2つの方法です。

- `$exceptionHandler`を上書き(override)する方法
    - 公式サイトではこちらの例が紹介されています。
- `$exceptionHandler`を装飾(decorator)する方法
    - 海外のサイトではこちらが多い印象でした。

## 注意事項

- 上書き(override)するともともと`$exceptionHandler`がデフォルトで持っている処理(※1)が消えてしまうため、残したい場合は同等の処理を記述するか装飾(decorator)で対応する必要があります。

- 公式サイトに書いてあるのですが、`$exceptionHandler`はAngular監視外のエラーは検知できないようなので、try-catchでエラーを検知したりerrorオブジェクトを生成して自分でthrowする必要があります(⇒余談①：throw Error)。


# 実装方法
## 上書き(override)

### 公式

>The example below will overwrite the default `$exceptionHandler` in order to (a) log uncaught errors to the backend for later inspection by the developers and (b) to use `$log.warn()` instead of `$log.error()`.

```js:overrideExample.js
angular.
  module('exceptionOverwrite', []).
  factory('$exceptionHandler', ['$log', 'logErrorsToBackend', function($log, logErrorsToBackend) {
    return function myExceptionHandler(exception, cause) {
      logErrorsToBackend(exception, cause);
      $log.warn(exception, cause);
    };
  }]);
```
引用元:[AngularJS: API: $exceptionHandler ](https://docs.angularjs.org/api/ng/service/$exceptionHandler)


ちょっと内容が変わっていますがTypeScriptでシンプルにかくとこんな感じでしょうか。

```ts:overrideExample.ts
angular.
    module('exceptionOverwrite', []).
    factory('$exceptionHandler', () => {
        return (exception: Error, cause: string) => {
            //エラー発生時に実行したい処理
            alert(exception.message);//アラートで表示
            console.warn(exception.message);//コンソールで表示
        };
    })
```


[buildinsiderのサイト](http://www.buildinsider.net/web/angularjstips/0042)でもサンプルを見ることができます。

## 装飾(decorator)

一例です。

```ts:decoratorExample.ts
angular.
    module('exceptionOverwrite', [])
    .config(['$provide', ($provide: ng.auto.IProvideService) => {
        $provide.decorator('$exceptionHandler',
            ['$delegate', extendExceptionHandler]);
    }]);

function extendExceptionHandler($delegate: ng.IExceptionHandlerService): ng.IExceptionHandlerService{
    return (exception: Error, cause: string) => {
        $delegate(exception, cause);
        //エラー発生時に実行したい処理
        alert(exception.message);//アラートで表示
        console.warn(exception.message);//コンソールで表示
    };
}
```
# 余談①：throw Error
任意の場所でエラーを発生させたいとき

```ts:error.ts
var error: Error = {
    name: "hogehoge",
    message: "エラーが発生しました。"
};
throw error;
```
# 余談②：`Error: $injector:cdep`
httpサービス使おうとしたときに循環参照発生したので回避策(extendExceptionHandler)

```ts:hoge.ts
function extendExceptionHandler($delegate: ng.IExceptionHandlerService): ng.IExceptionHandlerService{
    return (exception: Error, cause: string) => {
        $delegate(exception, cause);

        var $injector = angular.injector(['ng']);
        var $http:ng.IHttpService = $injector.get('$http');
        $http.post('hoge/hoge', exception);
    };
}
```

# 参考
[AngularJS: API: $exceptionHandler ](https://docs.angularjs.org/api/ng/service/$exceptionHandler)
[AngularJS Exception Logging Made Simple](https://www.loggly.com/blog/angularjs-exception-logging-made-simple/)　
[Github:exception-handler.provider.ts](https://github.com/johnpapa/hottowel-angular-typescript/blob/master/src/client/app/blocks/exception/exception-handler.provider.ts)
[stackoverflow:how-to-override-exceptionhandler-implementation](http://stackoverflow.com/questions/13595469/how-to-override-exceptionhandler-implementation)