5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

angularJSのcontrollerAsメモ

Last updated at Posted at 2016-01-18

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を使った場合の、ユニットテストなども記載してないので加筆、修正していきたいと思います。

5
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?