原文: http://blog.thoughtram.io/angularjs/2015/01/02/exploring-angular-1.3-bindToController.html
Angular1.2引入了新的语法 controllerAs, 它让scope更加清楚、干净, 让controller更加聪明. 在我们的Angular应用中使用controllerAs可以避免开发者经常遇到的一些问题.
controllerAs
做为命名空间
让我们用代码说事,有两个controller如下:
function ControllerOne($scope) { $scope.foo = 'Pascal'; } function ControllerTwo($scope) { $scope.foo = 'Christoph'; } app.controller('ControllerOne', ControllerOne); app.controller('ControllerTwo', ControllerTwo);
HTML如下:
<div ng-controller="ControllerOne"> {{foo}} <div ng-controller="ControllerTwo"> {{foo}} </div> </div>
ControllerTwo中的{{foo}}会覆盖ControllerOne中的
{{foo}}
, 第一个{{foo}}会显示'Pascal', 第二个{{foo}}显示'Christoph'.
如果我们想在ControllerTow中显示'Pascal'可以使用scope的$parent属性去引用父作用域:
<div ng-controller="ControllerOne"> {{foo}} <div ng-controller="ControllerTwo"> {{$parent.foo}} </div> </div>
然而使用$parent不是一个好的解决方案, 因为如果嵌套一多我们的代码就不利于维护了. 想象下如果你有4层或5层嵌套. 你的代码会变成$parent.$parent.$parent.$parent, 这是不是很糟糕?
首先我们使用this替代$scope:
function ControllerOne() { this.foo = 'Pascal'; } function ControllerTwo() { this.foo = 'Christoph'; }
接下来, 我们修改ngController指令,使用controllerAs语法:
<div ng-controller="ControllerOne as ctrl1"> {{ctrl1.foo}} <div ng-controller="ControllerTwo as ctrl2"> {{ctrl2.foo}} </div> </div>
当使用controller的时候,我们就可以使用controllerAs. 例如在Angular的$routeProvider中我们也可以使用controllerAs.
$routeProvider.when('/', { templateUrl: 'stateTemplate.html', controllerAs: 'ctrl', controller: 'StateController' });
指令也可以有controller, 所以我们在定义指令的时候也可以使用controllerAs.
app.controller('SomeController', function () { this.foo = 'bar'; }); app.directive('someDirective', function () { return { restrict: 'A', controller: 'SomeController', controllerAs: 'ctrl', template: '{{ctrl.foo}}' }; });
在指令中使用controllerAs的问题
我们说过,当使用controllerAs的时候controller的scope绑定到controller的this对象, 也就是说this代表我们的scope. 那么当指令有自己隔离的scope时,它会如何工作呢?
下面的指令有一个隔离的scope,一个controller和一个使用controller属性的template:
app.directive('someDirective', function () { return { scope: {}, controller: function () { this.name = 'Pascal' }, controllerAs: 'ctrl', template: '<div>{{ctrl.name}}</div>' }; });
如果这个name要进行双向绑定该怎么做呢?
app.directive('someDirective', function () { return { scope: { name: '=' }, // ... }; });
如果我们在外部修改隔离scope的属性,他不会反应到controller的this对象中去. 在AngularJS1.2中, 当属性变化的时候我们使用$scope服务显示的重新对我们的scope赋值. 不要忘了把$watch的callback绑定到controller的this:
app.directive('someDirective', function () { return { scope: { name: '=' }, controller: function ($scope) { this.name = 'Pascal'; $scope.$watch('name', function (newValue) { this.name = newValue; }.bind(this)); }, // ... }; });
等等,开始去掉的$scope 现在又回来了. 而且现在为了进行双向绑定我们额外写了许多代码.
幸运的是, 这些在AngularJS 1.3中得到了很好的解决!
使用bindToController绑定controller
Angular 1.3在指令中引入了一个新的属性bindToController. 当设置bindToController为true时,属性是绑定到controller而不是scope.
这意味, 当controller实例化, 隔离scope的初始值绑定到this上, 将来对这些属性值的改变也会自动反应.
app.directive('someDirective', function () { return { scope: { name: '=' }, controller: function () { this.name = 'Pascal'; }, controllerAs: 'ctrl', bindToController: true, template: '<div>{{ctrl.name}}</div>' }; });
看, 现在不需要$scope了.
1.4中的改善
在1.4中
, bindToController更加强大
.在1.4中, 我们可以把所有要绑定到隔离scope的属性移到bindToController中.
下面的例子中. 不再在scope中定义scope属性了:
app.directive('someDirective', function () { return { scope: {}, bindToController: { someObject: '=', someString: '@', someExpr: '&' } controller: function () { this.name = 'Pascal'; }, controllerAs: 'ctrl', template: '<div>{{ctrl.name}}</div>' }; });