Angular 1.5+ 之component()组件创建方法

component

在AngularJS中,组件是一种特殊类型的指令。Angular 1.5 引入了 .component() 辅助方法, 它的定义比 .directive() 更简单。 .component() 允许开发者以更接近Angular2的方式写Angular1的代码,方便开发者后续可以较平滑的升级Angular。

[TOC]

从.directive() 到.component()

以下是它们的语法差异:

1
2
3
4
5
// before
module.directive(name, fn);
// after
module.component(name, options);

先用.directive写一个简单的计数器,下边再用.component重构它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
.directive('counter', function counter() {
return {
scope: {},
bindToController: {
count: '='
},
controller: function () {
function increment() {
this.count++;
}
function decrement() {
this.count--;
}
this.increment = increment;
this.decrement = decrement;
},
controllerAs: 'counter',
template: [
'<div class="todo">',
'<input type="text" ng-model="counter.count">',
'<button type="button" ng-click="counter.decrement();">-</button>',
'<button type="button" ng-click="counter.increment();">+</button>',
'</div>'
].join('')
};
});

jsbin: 查看

方法名改变,并且Function 参数变为 Object

开始自上而下重构这个示例:

1
2
3
4
5
6
7
8
9
10
11
// before
.directive('counter', function counter() {
return {
};
});
// after
.component('counter', {
});

.directive 中本质上需要返回一个函数,而 .component 只需要传一个对象了。

scope 和 bindToController 变为 bindings

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// before
.directive('counter', function counter() {
return {
// scope 用于创建独立作用域或继承父级作用域
// 由于这个选项用来创建独立作用域基本上是不可或缺的,所以每次都要写就很繁琐了
scope: {},
// bindToController 可以直接定义那些想传入独立作用域的属性,并把它们绑定到controller上
bindToController: {
count: '='
}
};
});
// after
.component('counter', {
// 用bindings可以简单地定义要传递哪些属性到component中,且并component拥有独立作用域
bindings: {
count: '='
}
});

Controller 和 controllerAs 的变化

在定义controller的方式上倒没有什么变化,唯一一点不同就是 controllerAs 多了一个默认值: $ctrl

在directive中定义controller

1
2
3
4
5
{
...
controller: function () {}
...
}

或者在别的地方定义,此处引用

1
2
3
4
5
{
...
controller: 'otherCtrl'
...
}

又或者用 controllerAs 起别名

1
2
3
4
5
6
{
...
controller: 'otherCtrl'
controllerAs: 'other'
...
}

然后就可以在模板中使用 other.prop之类来访问Controller实例。

.component() 中就不会那么麻烦了,在我们没明确指定Controller实例别名时,它会自动用3种方式创建 controllerAs 属性,Angular中相关源码如下:1

1
controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',

其中第一个 identifierForController中controller属性为字符串时(controller: ‘SomeCtrl as something’), 提取 as 后边的名字,源码如下:

1
2
3
4
5
6
7
8
var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
function identifierForController(controller, ident) {
if (ident && isString(ident)) return ident;
if (isString(controller)) {
var match = CNTRL_REG.exec(controller);
if (match) return match[3];
}
}

第二个 controllerAs 用于当controller属性为function时的情况。

第三个 $ctrl 默认值让我们可以忽略掉 controllerAs 了,所以:

1
2
3
4
5
6
.component('test', {
controller: function () {
// 所以可以直接在模板中通过 $ctrl.testing访问
this.testing = 123;
}
});

说了那么多,终于可以在重构中把 controllerAs 干掉了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// before
.directive('counter', function counter() {
return {
scope: {},
bindToController: {
count: '='
},
controller: function () {
function increment() {
this.count++;
}
function decrement() {
this.count--;
}
this.increment = increment;
this.decrement = decrement;
},
controllerAs: 'counter'
};
});
// after
.component('counter', {
bindings: {
count: '='
},
controller: function () {
function increment() {
this.count++;
}
function decrement() {
this.count--;
}
this.increment = increment;
this.decrement = decrement;
}
});

可见指令的定义变得更加简洁。

Template

接下来我们添加template来完成我们的重构工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.component('counter', {
bindings: {
count: '='
},
controller: function () {
function increment() {
this.count++;
}
function decrement() {
this.count--;
}
this.increment = increment;
this.decrement = decrement;
},
template: `
<div class="todo">
<input type="text" ng-model="$ctrl.count">
<button type="button" ng-click="$ctrl.decrement();">-</button>
<button type="button" ng-click="$ctrl.increment();">+</button>
</div>
`
});

需要注意的是,我们也可以把template定义为一个注入了$element$attrs的函数。如果template被定义为函数,需要返回HTML字符串文本编译。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
...
template: function ($element, $attrs) {
// access to $element and $attrs
return `
<div class="todo">
<input type="text" ng-model="$ctrl.count">
<button type="button" ng-click="$ctrl.decrement();">-</button>
<button type="button" ng-click="$ctrl.increment();">+</button>
</div>
`
}
...
}

至此,我们的重构工作完成了,当然这些只是接触到了一部分component的内容,有些地方我们需要更多的讨论。

jsbin: 查看

参考

经不住似水流年  逃不过此间少年
0%