Angular是一个非常强大的框架, 有时过于强大会导致一些开发人员犯一些架构错误. 双向数据绑定和指令的强大功能令人敬畏, 但是您需要考虑您在做什么,并尝试使用一些最佳实践来避免开发过程中的常见陷阱.
控制器是类类对象,用来“控制”模型和更新视图, 正如你所知,一切都是基于魔法和神秘 $scope
property.
一个好的做法是避免将所有内容绑定到 $scope
的监视列表中挤满了太多的绑定 $digest
loop. 为了避免这种情况,Angular给了我们 controllerAs
property.
Writing controllers as classes
Javascript中的类(至少目前在ES5中)是这样的:
var aClass = function () {
this.name = 'Class name';
};
var instance = new aClass();
With this you can use the instance
变量来访问方法和属性.
Using the controllerAs
属性,我们用同样的方式编写控制器,使用 this
instead of $scope
angular.module('myApp')
.控制器('MyCtrl', function () {
this.name = 'Controller Name';
});
现在它可以在模板中实例化,如下所示:
要访问控制器的属性和方法,请使用 vm
instance.
这样就为作用域设置了名称空间,使代码更清晰易读. Think about nested scopes.
这里你可以看到每个控制器都在访问 name
财产,但问题是,哪一个? 这段代码看起来很混乱,可能是一个控制器优先于另一个控制器, but you don’t know which one.
Using the controllerAs
这样语法会清晰得多:
Base scope: {{ base.name }}
Section scope: {{ section.name }}
Base scope: {{base.name}}
{{ final.name }}
正如我们在上面的代码中看到的,使用 controllerAs
语法允许我们访问父作用域,而没有作用域冲突的麻烦,也不用使用 $parent
to access it.
How to set watchers
当您使用这种语法时,您会想到的一个问题是如何使用 $watch
打电话是因为你需要注射 $scope
. We fight to remove the use of $scope
现在无论如何我们都要注射它.
Well, we can keep using controllerAs
并将方法和属性绑定到 this
对象,该对象绑定到当前 $scope
. 同时,我们可以保持关注点分离 $scope
only for special cases, like $watch
, $on
, or $broadcast
.
Keep in mind that using controllerAs
the syntax for $watch
method changes a little bit. 通常你会这样做:
app.控制器('Ctrl', function ($scope) {
$scope.name = 'name';
$scope.$watch('name', function (newVal, oldVal) {
});
});
但现在不管用了,因为 $watch
是否在寻找监视属性 $scope
,你不会直接绑定那个属性 $scope
. 被监视的属性被绑定到 this
. 现在正确的做法如下例所示:
app.控制器('Ctrl', function ($scope) {
this.name = 'name';
$scope.$watch(function () {
return this.title
}.bind(this), function (newVal, oldVal) {
});
});
Alternative is using angular.bind
:
app.控制器('Ctrl', function ($scope) {
this.name = 'name';
$scope.$watch(angular.bind(function () {
return this.title
}), function (newVal, oldVal) {
});
});
How can I declare controllerAs
不使用DOM属性?
在指令的情况下,有 controllerAs
属性在指令签名中:
app.指令('指令',function () {
return {
restrict: 'EA',
templateUrl: 'template.html',
scope: true,
controller: function () {},
controllerAs: 'vm'
}
});
Or for controllers in the $routeProvider
:
app.配置(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'main.html',
controllerAs: 'main',
controller: 'MainCtrl'
})
});