angularJSのcontrollerAsメモ

##controllerAs を使って$scopeを使わない方法

##はじめに
通常controllerからviewを操作する場合は、``$scope``の値を操作します。
しかし、``$scope``の継承がわかりずらく、思わぬバグを起こす場合もあります。
また、``$scope``はAngular2.0では廃止される予定のようです。
そこで、``controllerAs``を使って``$scope``を使わない書き方も学んでおこうと思います。

##$scopeとcontrollerAs

``$scope``は、angularjsが提供するオブジェクトで、viewに公開する変数や関数を定義する。
コントローラーで、$scopeをインジェクトして利用する。

controllerAsを使うと、コントローラーをスコープオブジェクトとして利用することができる。


##使い方

controllerAsの使い方は簡単です。

controller名を指定する際に、``ng-controller="コントローラー名 as 別名"``のように指定します。
cotnroller側では$scopeの代わりに``this``を使用します。
html側では``別名.プロパティ名``のように使用します。

```lang:html
<html ng-app="App" ng-controller="MainCtrl as main">
<body>
  {{main.name}}
</body>
</html>
```

```lang:javascript
var app = angular.module('App', []);

app.controller('MainCtrl', [function() {
  this.name = '太郎';//scopeを使う場合は、$scope.name = '太郎'
}]);
```

<a href="http://codepen.io/satofaction/pen/mVPdGO?editors=101" target="_blank">サンプル</a>

##controllerAsを使った場合に考えること

### $watchはどうなるのか

$scopeのプロパティの変更を監視する$watchメソッドですが、$scopeのプロパティ外のものを監視する場合は下記のような方法になるようです。

```lang:javascript
var app = angular.module('App', []);

app.controller('MainCtrl', ['$scope', function($scope) {
  
   //スコープの値を設定
  this.name = '太郎';
  
  //スコープの値を変更
  this.changeName = function() {
    this.name = this.name === '太郎'? '花子':'太郎';
  }
  
  //thisをコンテキストとして束縛
  $scope.$watch(angular.bind(this,function(){
    return this.name;
  }), function() {
    console.log('値が変更されました')
  });

}]);
```

$watchは第一引数に、監視対象のプロパティの文字列か、監視対象の値を返す関数を登録できます。

下記のように単純にfunctionを第一引数に登録すると上手く動きません。
理由としては、ネストされた関数の中のthisはグローバルを参照するからです。

```lang:javascript
var app = angular.module('App', []);

app.controller('MainCtrl', ['$scope', function($scope) {
  
   //スコープの値を設定
  this.name = '太郎';
  
  //スコープの値を変更
  this.changeName = function() {
    this.name = this.name === '太郎'? '花子':'太郎';
  }

 $scope.$watch(function(){
   //このthisはコントローラーのスコープではない
   return this.name;
 }, function() {
   console.log('値が変更されました')
 });
}]);
```

その為、fanctionのコンテキストを``angular.bind``を使用して束縛する必要があります。

<a href="http://codepen.io/anon/pen/gPWYvg?editors=101" target="_blank">サンプル</a>

angular.bindを使用しなくても下記のようにthisを変数にいれておいて使用すれば
問題なく動作します。

```lang:javascript
var app = angular.module('App', []);

app.controller('MainCtrl', ['$scope', function($scope) {

　 //thisを変数にいれておく
  var self = this;

   //スコープの値を設定
  this.name = '太郎';
  
  //スコープの値を変更
  this.changeName = function() {
    this.name = this.name === '太郎'? '花子':'太郎';
  }

 $scope.$watch(function(){
   return self.name;
 }, function() {
   console.log('値が変更されました')
 });
}]);
```

### $applyはどうなるのか

``$scope.$apply``で``$scope``の変更をviewに反映する場合がありますが、controllerASを使用した場合、
thisの変更をviewに反映するにはどうしたらいいのかなと疑問を持ちました。

下記のように、thisの値を変更して``$scope.$apply``を実行したところ問題なくviewにthisの変更が反映されました。

```lang:javascript
app.controller('MainCtrl', ['$scope', function($scope) {
  this.name = '太郎';
  setTimeout(angular.bind(this, function() {
    this.name = '花子';
    $scope.$apply();
  }), 500);
}]);
```

```lang:html
<html ng-app="App" ng-controller="MainCtrl as main">
<body>
  {{main.name}}
</body>
</html>
```

####なぜ$applyで反映されたのか
``$scope``の中身を確認したところ``$$watchers``にthisで定義した値ぽいオブジェクトが追加されていました。
これで``$apply()``が実行された際に``$watch``式が評価され反映がされていたようです。
※理解が浅いために正しく説明できていません。

```
$$watchers: [{
eq: false
exp: (a)
fn: (a,c,f)
get: (a)
last: "花子"
}]
```
<a href="http://codepen.io/satofaction/pen/ZQyRKa" target="_blank">サンプル</a>

###おわり
$watch, $digest, $apply あたりの理解が浅いことで、あまりまとまった内容になりませんでした。
controllerAsを使った場合の、ユニットテストなども記載してないので加筆、修正していきたいと思います。