angular学习(三)—— Controller


在angular中,controller由javascript的构造函数定义,主要用于增强angular的scope

Controller介绍

在angular中,controller由javascript的构造函数定义,主要用于增强angular的scope。

当controller通过ng-controller directive与DOM关联,angular将用指定的controller构造函数实例化一个新的controller对象,同时一个新的child scope将被创建,然后以参数$scope注入到controller中。

如果controller使用了controller as a语法,那么控制器实例将会分配给这个属性a。在第一章有讲到这种情况,可以返回去看下,这里就不再写示例了。

使用controller的情况:

  • $scope中对象的初始化
  • 给$scope中对象增加一些行为

不使用controller的情况:

  • 操作DOM,controller应该仅仅包含业务逻辑,把显示的逻辑放到controller中会影响它的可测试性,angular有很多数据绑定和封装了DOM操作的directives,完全没必要去操作DOM。
  • 格式化输入,用angular form controls代替
  • 过滤输出,用angular filters代替
  • 在controller直接共享代码或状态,用angular services代替
  • 管理其他组件的生命周期(如创建一个service实例)

scope对象初始化

通常情况下,你创建一个angular应用时,你需要先设置$scope的初始状态。你需要为$scope设置一些属性,包含在view中预先声明的model,所有$scope的属性在controller注册的DOM里都是可用的。

下面的例子演示如何创建一个controller,并且初始化scope对象。

1
2
3
4
5
var myApp = angular.module('myApp', []);
myApp.controller('GreetingController', ['$scope', function ($scope) {
$scope.greeting = 'Hola!';
}]);

我们创建了一个angular module : myApp,然后调用module的controller方法,给module增加了一个controller构造函数。

再看下controller相对应的DOM,greeting属性做了数据绑定,可以显示在controller中的值。

1
2
3
<div ng-controller="GreetingController">
{{ greeting }}
</div>

给scope对象增加一些行为

为了响应一些事件或者在view中进行一些计算,我们必须为\$scope增加一些行为。我们通过为\$scope对象增加方法的方式为他增加行为,这些方法可以被template/view调用。

下面的示例演示如何为controller增加方法

1
2
3
4
5
var myApp = angular.module('myApp',[]);
myApp.controller('DoubleController', ['$scope', function($scope) {
$scope.double = function(value) { return value * 2; };
}]);

controller一旦被添加到DOM中,该方法就可以在template中被调用

1
2
3
<div ng-controller="DoubleController">
Two times <input ng-model="num"> equals {{ double(num) }}
</div>

Controller示例

为了进一步说明angular controller是如何工作的,我们再来一个程序,包含下面一些组件:

  • 一个template,包含2个按钮和一条消息
  • 一个model,包含字符串spice
  • 一个controller,包含2个位spice赋值的函数

在模板中的消息包含一个spice model,默认设置为very,点击按钮时给spice赋值,通过data binding的方式自动更新这条消息。
index.html

1
2
3
4
5
<div ng-controller="SpicyController">
<button ng-click="chiliSpicy()">Chili</button>
<button ng-click="jalapenoSpicy()">Jalapeño</button>
<p>The food is {{spice}} spicy!</p>
</div>

app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
var myApp = angular.module('spicyApp1', []);
myApp.controller('SpicyController', ['$scope', function($scope) {
$scope.spice = 'very';
$scope.chiliSpicy = function() {
$scope.spice = 'chili';
};
$scope.jalapenoSpicy = function() {
$scope.spice = 'jalapeño';
};
}]);

输出结果
这里写图片描述
要注意以下几点:

  • ng-controller directive 隐式地为template创建了一个scope,这个scope又被SpicyController controller管理。
  • 仅仅是一个简单的javascript函数。命名规范是大写字母开头,以Controller结束。
  • 为scope分配一个属性去创建和更新model
  • controller的方法可以通过直接赋值的方式创建
  • controller的方法或者属性在template中是可用的

带参数的Controller方法示例

controller的方法也可以带参数,我们来修改一下上面的示例

index.html

1
2
3
4
5
6
<div ng-controller="SpicyController">
<input ng-model="customSpice">
<button ng-click="spicy('chili')">Chili</button>
<button ng-click="spicy(customSpice)">Custom spice</button>
<p>The food is {{spice}} spicy!</p>
</div>

app.js

1
2
3
4
5
6
7
8
9
10
var myApp = angular.module('spicyApp2', []);
myApp.controller('SpicyController', ['$scope', function($scope) {
$scope.customSpice = "wasabi";
$scope.spice = 'very';
$scope.spicy = function(spice) {
$scope.spice = spice;
};
}]);

SpicyController仅仅定义了一个spicy方法,这个方法需要传入spice参数。第一个按钮固定传入一个chili的参数,第二个按钮传入一个与输入框绑定的model 属性customSpice。

Scope继承示例

controller被使用在不同的DOM层次结构中是很常见的。一旦ng-controller directive创建了一个子的scope,那么就得到了一个相互继承的scope层次结构。每一个controller的\$scope都可以访问比他更高层级controller定义的属性和方法。
index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="spicy">
<div ng-controller="MainController">
<p>Good {{timeOfDay}}, {{name}}!</p>
<div ng-controller="ChildController">
<p>Good {{timeOfDay}}, {{name}}!</p>
<div ng-controller="GrandChildController">
<p>Good {{timeOfDay}}, {{name}}!</p>
</div>
</div>
</div>
</div>

app.js

1
2
3
4
5
6
7
8
9
10
11
12
var myApp = angular.module('scopeInheritance', []);
myApp.controller('MainController', ['$scope', function($scope) {
$scope.timeOfDay = 'morning';
$scope.name = 'Nikki';
}]);
myApp.controller('ChildController', ['$scope', function($scope) {
$scope.name = 'Mattie';
}]);
myApp.controller('GrandChildController', ['$scope', function($scope) {
$scope.timeOfDay = 'evening';
$scope.name = 'Gingerbread Baby';
}]);

app.css

1
2
3
4
div.spicy div {
padding: 10px;
border: solid 2px blue;
}

这里创建了三个ng-controller directives,这样就为view创建了四个scope:

  • root scope
  • MainController scope,包含了timeOfDay和name两个属性
  • ChildController scope,继承了timeOfDay但覆盖了name属性
  • GrandChildController scope,覆盖了timeOfDay和name两个属性

Controller中方法的继承和属性一样,因此我们之前例子的属性都可以被返回字符串的方法代替。

Controller测试

Controller Definition:

1
2
3
4
5
6
7
8
var myApp = angular.module('myApp',[]);
myApp.controller('MyController', function($scope) {
$scope.spices = [{"name":"pasilla", "spiciness":"mild"},
{"name":"jalapeno", "spiciness":"hot hot hot!"},
{"name":"habanero", "spiciness":"LAVA HOT!!"}];
$scope.spice = "habanero";
});

Controller Test

尽管有很多种方法测试angular,但最好的一种就是通过注入\$rootScope和\$controller的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
describe('myController function', function() {
describe('myController', function() {
var $scope;
beforeEach(module('myApp'));
beforeEach(inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
$controller('MyController', {$scope: $scope});
}));
it('should create "spices" model with 3 spices', function() {
expect($scope.spices.length).toBe(3);
});
it('should set the default value of spice', function() {
expect($scope.spice).toBe('habanero');
});
});
});

如果你想测试一个嵌套的controller,那你必须创建一样嵌套的scope。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
describe('state', function() {
var mainScope, childScope, grandChildScope;
beforeEach(module('myApp'));
beforeEach(inject(function($rootScope, $controller) {
mainScope = $rootScope.$new();
$controller('MainController', {$scope: mainScope});
childScope = mainScope.$new();
$controller('ChildController', {$scope: childScope});
grandChildScope = childScope.$new();
$controller('GrandChildController', {$scope: grandChildScope});
}));
it('should have over and selected', function() {
expect(mainScope.timeOfDay).toBe('morning');
expect(mainScope.name).toBe('Nikki');
expect(childScope.timeOfDay).toBe('morning');
expect(childScope.name).toBe('Mattie');
expect(grandChildScope.timeOfDay).toBe('evening');
expect(grandChildScope.name).toBe('Gingerbread Baby');
});
});

文章目录
  1. 1. Controller介绍
    1. 1.1. scope对象初始化
    2. 1.2. 给scope对象增加一些行为
  2. 2. Controller示例
  3. 3. 带参数的Controller方法示例
  4. 4. Scope继承示例
  5. 5. Controller测试
    1. 5.1. Controller Definition:
    2. 5.2. Controller Test
|