Help us understand the problem. What is going on with this article?

angularJSのcontrollerAsメモ

More than 3 years have passed since last update.

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側では別名.プロパティ名のように使用します。

html
<html ng-app="App" ng-controller="MainCtrl as main">
<body>
  {{main.name}}
</body>
</html>
javascript
var app = angular.module('App', []);

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

サンプル

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

$watchはどうなるのか

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

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はグローバルを参照するからです。

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を使用して束縛する必要があります。

サンプル

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

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の変更が反映されました。

javascript
app.controller('MainCtrl', ['$scope', function($scope) {
  this.name = '太郎';
  setTimeout(angular.bind(this, function() {
    this.name = '花子';
    $scope.$apply();
  }), 500);
}]);
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: "花子"
}]

サンプル

おわり

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

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away