转载请写明来源地址:http://blog.csdn.net/lastsweetop/article/details/56285838
理解Components
在AngularJS中,Component是一种特殊的directive,它的配置更简单一些,非常适合组件化的app架构。使用web组件和使用Angular风格的app架构使得编写app更为简便。
Component的优点:
- 比普通directive要简单很多
- 更加严谨,更加规范化
- 更加适合组件化架构
- component更容易升级到angular2
不使用Component的情况
- 需要在编译阶段和预链接阶段执行的directive,因为Component这时还不可用
- 当你需要directive才有定义的选项时,如priority, terminal, multi-element
- 当你需要directive由属性,css的class而不是元素触发时
Components的创建和配置
Components由angularjs的module使用.component()
方法注册。这个方法接受2个参数:
- Component的名称(字符串类型)
- Component的配置对象(注意,和
.directive()
不一样,不是一个工厂方法而仅仅是个配置对象)
heroApp.html:
- 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
- 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
heroDetail.html:
- 1
- 1
Directive和Component之间的定义比较
属性 | Directive | Component |
---|---|---|
bindings | No | Yes (binds to controller) |
bindToController | Yes (default: false) | No (use bindings instead) |
compile function | Yes | NO |
controller | Yes | Yes (default function() {}) |
controllerAs | Yes (default: false) | Yes (default: $ctrl ) |
link functions | Yes | No |
multiElement | Yes | No |
priority | Yes | No |
replace | Yes (deprecated) | No |
require | Yes | Yes |
restrict | Yes | No (restricted to elements only) |
scope | Yes (default: false) | No (scope is always isolate) |
template | Yes | Yes, injectable |
templateNamespace | Yes | No |
templateUrl | Yes | Yes, injectable |
terminal | Yes | No |
transclude | Yes (default: false) | Yes (default: false) |
组件化app架构
如前所述,component使得使用组件化架构构建app更为容易,除此之外component还具备的能力具体如下:
- Component只能控制它自己的视图和数据:Component不会修改它自身scope之外的任何数据或DOM。通常情况下,AngularJS中可以通过scope继承和watch可以修改任何地方的数据,这确实很实用,但是也会导致很难明确哪个部分对修改数据负责。这就是为什么Component使用隔离范围,也因此无法进行所有scope的操作。
-
Component有明确定义的公共api-输入输出:隔离范围并不是全部,因为AngularJS是双向绑定的。如果你传一个对象到组件中,类似
bindings: {item: '='}
,然后修改对象的属性,修改会反映到它的父组件中。但是对于component来说,component确实只是修改了它自己的scope内的数据。这样就很清晰的得知什么数据什么时候被修改。就此,component遵循一些简单的约定:-
输入需要使用
<
和@
绑定。<
符号在1.5之后表示单向绑定,和=
不同的是,它绑定的属性在component的scope哪不会被watch,这意味着你可以在component的scope内给属性设置一个新的值,它并不会更新父scope里的值。但是如果父scope和component的scope引用的是同一个对象,比如你在component修改对象的属性或者数组中的元素,这种改变仍然会反映到父scope。因此在<
绑定后不要在component内修改对象的属性和数组的元素。当输入的值是字符串时可以使用@
绑定,特别是在绑定的值不会更改的时候。- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
-
输出使用
&
进行绑定,绑定的函数将作为component事件的回调函数- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
-
在不操作输入数据的情况下,component可以调用相应的输出事件改变输出数据。比如删除,并不是hero自己删除自己,而是通过相应的事件把自己传给父component
- 1
- 2
- 3
- 1
- 2
- 3
-
这样,父component来决定事件执行什么(删除item还是更新属性)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
-
-
Component有明确定义的生命周期:每个组件都可以实现
lifecycle hooks
,这些方法会在component生命周期的相应的点被调用,可以实现的hook方法如下:$onInit()
- 在element上的所有controller构造和所有的绑定初始化之后,在element之上前缀&的函数链接之前,每一个controller调用这个钩子方法。这是个给controller写一些初始化方法的好地方$onChanges(changesObj)
- 当单向绑定更新时调用,changesObj是一个hash键值对,key是已被修改的绑定属性的name,value是一个对象,格式是{ currentValue, previousValue, isFirstChange() }
,使用这个hook可以只触发组件内的更新,比如克隆绑定属性的对象以防止它被外部意外更新$doCheck()
- 在每一个digest循环被调用,提供了一个机会可以在数据更改时验证或者做一些操作。任何你希望进行的操作(比如对更改的响应)都必须通过这个hook调用。当$onChanges
被调用时,实现这个hook没什么作用。比如,比如你想实现一个深度的equal函数检查,或者检查一个Date对象时,他将会非常有用,因为这是AngularJS的变化检测器检测不到这个变化,自然$onChanges
也不会被调用。这个hook不包含任何参数,因此,如果想要检测变化,你需要存储之前的值,然后和现在的值进行比较。$onDestroy()
- 当controller包含的scope销毁时,controller会调用这个hook。使用这个hook可以释放一些外部资源,watch和事件处理程序$postLink()
- 在controller的element和子元素被链接后调用。和post-link函数类似,这个hook可以设置DOM事件的处理程序或者直接进行DOM操作。包含templateUrl指令的element不会被编译和链接,因为它们要等template异步加载。它们的编译和链接要等template完成之后才会执行。这个hook非常和angular2中的ngAfterViewInit和ngAfterContentInit两个hook类似。因为angular1和angular2编译过程的不同,所以在anguar1升级到angular2时一定要小心。
通过实现这些方法,你的component可以在生命周期中hook
-
app就是一棵component树:理想的情况下,整个app是一棵component构成的树,清晰的定义了输入输出,并尽量不使用双向绑定。这样更容易预测数据的变化和组件状态。
Component树示例
下面这个示例对上面对例子进行了扩展,并和我们刚讲过到概念结合起来:
不再使用ngController,现在使用拥有多个hero的heroList组件象,为每个hero创建一个heroDetail组件。
heroDetail组件包含的新功能:
- 一个delete按钮,可以调用绑定到heroList组件的onDelete函数
- 一个可以改变hero的location的输入控件,该控件是可重用的editableField组件。不是editableField组件自己操作hero对象,它将改变传给heroDetail组件,heroDetail组件再传给heroList组件,由heroList组件更新原始数据。
heroApp.html
- 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
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 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
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
heroList.html
- 1
- 2
- 1
- 2
heroDetail.html
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
editableField.html
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
Component作为route模版
当使用ngRoute时,Component作为route的模版也是非常有用的。一个组件化的app,每一个视图都是一个组件:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
使用$routeProvider
时,你常常可以避开一些样板将已解决达route依赖传到component中。1.5版本之后,ngRoute可以自动分配resolve到route的scope属性$resolve
,你也可以通过resolveAs配置属性名。当用component时,你可以利用component的优势,直接把resolve传到你的component中,而不用创建额外的route控制器。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
Component间的通信
Directive需要其他Directive的controller来相互通信。在component中可以为require属性提供一个对象map来实现,map的key是所需controller构成的属性,而map的值是所需组件的名称。
不过要注意的是,所需的controller在它初始化完成之前是不可用的,但是可以确定的是当前controller在$onInit
执行之前所需的controller是可用的。
下面的例子是将上一篇文章由Directive方式改为了component方式:
docsTabsExample.html
- 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
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 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
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
my-tabs.html
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
my-pane.html