大規模なAngularJSをOOPにまとめる設計 - ES6 + Traceur

# 紹介

最近リリースしたAngular2が世を騒いている中、ES6を使ってAngularJS 1を書いてみました。

TraceurとES6は知らない人がいるかもしれないので、念のために簡単に説明すると、ESはEcmaScriptの略で元々はブラウザのscript言語ではありましたが、転々とした歴史で今となって仕様を決める組織になりました。今のブラウザで使われているjavascriptは基本にES5から派生したもので、いわばjavascriptの親でもあります。そして、今回使いたいES6はclassが書けるようになりました。今回の記事はES6を使って、AngularJSはどれだけ綺麗にソースコードを整理できるのかを紹介したいと思います。

最後にTraceurをご紹介します。TraceurはGoogleさんより開発されたES6をES5に変換するコンパイラーです。なぜ変換する必要があるかと言うと、まだES6が搭載されたブラウザが少ないため、実際に本番に落とすには安定なES5に変換しないといけません。

# インストール

まずnpmでTraceurをインストール
```
npm install -g traceur
```

# コーディングしてみましょう

下記の2つのファイルを作成

```javascript:index.js
'use strict';

class My {
    constructor($scope) {
        this._scope = $scope;

        this.hello = "hello hoge";
    }

    getMessage() {
        return "hoge message";
    }
}

angular.module("myapp",[])
    .controller("myCtrl", [ "$scope" ,My]);
```

```html:index.html
<html ng-app="myapp">
<head>
    <title></title>
    <script src="http://google.github.io/traceur-compiler/bin/traceur.js"></script>
</head>
<body ng-controller="myCtrl as m">
    <div ng-bind="m.getMessage()"></div>
    <div ng-bind="m.hello"></div>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
    <script type="text/javascript" src="build.js"></script>
</body>
</html>
```

まず、index.jsをコンパイルし、build.jsとして生成されるように次のコマンドを実行し

```
traceur index.js --out build.js
```

そうすると、EC5になったbuild.jsが生成されるので、実際にsafariとchromeで確かめると、hello hogeとhoge messageが見られることがわかります。

# 複数のjsファイルをコンパイル

大規模な設計を目指しているので、当然ながら複数のjsファイルが存在しているはずです。そのケースを次のようにまとめてみました。

ファイル構造

```
├ es6
│ ├ index.js
│ ├ apple.js
│ ├ banana.js
│ └ watermelon.js
├ build.js
└ index.html
```

```html:index.html
<html ng-app="myapp">
<head>
    <title></title>
    <script src="http://google.github.io/traceur-compiler/bin/traceur.js"></script>
</head>
<body>
    <ul>
        <li><a ui-sref="banana">Banana</a></li>
        <li><a ui-sref="apple">Apple</a></li>
        <li><a ui-sref="watermelon">Watermelon</a></li>
    </ul>
    

    <div ui-view></div>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
    <script type="text/javascript" src="dist/angular-ui-router.min.js"></script>
    <script type="text/javascript" src="build.js"></script>
</body>
</html>
```

```js:index.js
'use strict';
/*jshint esnext: true */
/*global angular: false */

import Banana     from "./banana.js";
import Apple      from "./apple.js";
import Watermelon from "./watermelon.js";


angular.module("myapp",["ui.router"])
    .config(function($stateProvider, $urlRouterProvider) {
        $urlRouterProvider.otherwise("/banana");

        $stateProvider
            .state('banana', {
                url: "/banana",
                controller: Banana,
                controllerAs: "vm",
                template: "<div ng-bind='vm.hello'></div>",
            })
            .state('apple', {
                url: "/apple",
                controller: Apple,
                controllerAs: "vm",
                template: "<div ng-bind='vm.hello'></div>",
            })
            .state('watermelon', {
                url: "/watermelon",
                controller: Watermelon,
                controllerAs: "vm",
                template: "<div ng-bind='vm.hello'></div>",
            });
    });
```

apple.jsと同様にwatermelon.jsとbanana.jsを作成しました。ただし、hello変数の中身だけをAppleからWatermelonなど各フルーツの名前に変えました。

```js:apple.js
'use strict';

class Apple {
    constructor() {
        this.hello = "hello this is Apple";
    }
}

export default Apple;
```

最後に、コンパイルします。

```
traceur es6/* --out build.js
```

すべてのES6のjsファイルをes6というフォルダに収納しているので、コンパイルするときにes6/*と指定すれば良いです。

### scopeは?

index.jsを見ると、scopeが存在しているが使われていません。`myCtrl as m`の代わりにscopeを使ってもいいのですが、せっかくclassを使うので、さらにAngular2にはscopeが消されたこともわかったし、scopeを使うことは控えました。

# 参考サイト

http://victorsavkin.com/post/99998937651/building-angular-apps-using-flux-architecture
http://www.sitepoint.com/writing-angularjs-apps-using-es6/
https://github.com/mvolkmann/todo-es6