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 ng-app="App" ng-controller="MainCtrl as main">
<body>
{{main.name}}
</body>
</html>
var app = angular.module('App', []);
app.controller('MainCtrl', [function() {
this.name = '太郎';//scopeを使う場合は、$scope.name = '太郎'
}]);
controllerAsを使った場合に考えること
$watchはどうなるのか
$scopeのプロパティの変更を監視する$watchメソッドですが、$scopeのプロパティ外のものを監視する場合は下記のような方法になるようです。
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はグローバルを参照するからです。
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を変数にいれておいて使用すれば
問題なく動作します。
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の変更が反映されました。
app.controller('MainCtrl', ['$scope', function($scope) {
this.name = '太郎';
setTimeout(angular.bind(this, function() {
this.name = '花子';
$scope.$apply();
}), 500);
}]);
<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を使った場合の、ユニットテストなども記載してないので加筆、修正していきたいと思います。