Angular 2 之三 组件
概述
Angular 2应用由组件构成;和DOM树类似,Angular 2应用是一颗组件树。
Angular 2将更新模型(Model)和在视图(View)中显示模型分在不同阶段实现:开发者负责更新模型,Angular 2通过变更检测自动更新对应的视图。
Angular 2使用属性绑定在视图中显示模型,使用事件绑定触发事件处理函数(相当于Control),事件处理函数中可以更新模型。
由此可见,Model-View-Control间的数据流是单向的,即:
- 变更检测自动将Model状态显示在View上
- 用户操作View触发Control
- Control中更改Model。
对组件而言,属性绑定是输入,使用@Input标注定义,[]语法调用;事件绑定是输出,使用@Output标注定义,()语法调用。属性绑定和事件绑定是父子组件间最常用交互方式。
组件定义
组件定义使用@Component标注,元数据说明如下:
- selector: 定义声明式组件的元素名称
- template: 视图模板字符串
- templateUrl: 视图模板URL,即外部模板文件
- styles: 本组件使用的样式表列表
- styleUrls: 本组件使用的样式表URL列表
- directives: 本组件模板用到的Directive(和Componet)列表
- pipes: 本组件模板用到的Pipe列表
- providers: 组件级依赖注入的服务列表
- viewProviders: 仅在本组件使用的依赖注入服务列表
- changeDetection: 变更检测策略,缺省是检测所有组件
- encapsulation: 使用Shadow DOM策略,缺省是ViewEncapsulation. Emulated
template和templateUrl两者使用其一,简单模板使用template即可。使用templateUrl会多一次HTTP请求,为改善性能使用Webpack时可采用以下方式: template: require(‘the_template.html’); Webpack能将HTML模板作为模块一起打包。 providers和viewProviders间有细微的差异,请参见4.5节说明。一般情况下,使用providers即可。
组件类的字段标注定义如下:
- @Input: 定义属性绑定的输入字段
- @Output: 定义事件绑定的输出字段用于触发事件
- @HostBinding: 将宿主元素的属性绑定到组件类成员字段
- @HostListener: 将组件类的成员函数注册到宿主元素的事件回调
- @ContentChild:用于绑定内容中单个子组件,参见示例
- @ContentChildren: 用于绑定内容中子组件列表
- @ViewChild: 用于绑定模板中的单个子组件
- @ViewChildren: 用于绑定模板中的子组件列表
组件分类
为了组件重用和逻辑清晰,可以将组件分为两类:
- 展示组件(Presentational Component):仅负责展现视图,一般没有应用状态
- 容器组件(Container Component):包括应用状态,实现应用行为。 该方法好处是:
- 更好地分离UI和逻辑
- 展示组件重用性好
- 设计师在界面上调整展示组件属性和组件功能无关
- 迫使开发人员抽象出布局组件,而不是到处使用类似的布局代码。
模板语法 Template Syntax
模板语法用于定义组件模板,即@Component标注的template或templateUrl内容。
1. HTML
HTML元素可用于模板,script除外。一般地,html、body和base不会用于模板中。
2. 插值 Interpolation
插值使用{{和}}括起来,括号间内容叫做模板表达式(Template Expression)。插值计算为字符串加在HTML元素或者属性值中,例如:
<h3> {{ title }} <img src="{{ heroImageUrl }}"/> </h3>
3. 模板表达式 Template Expression
模板表达式很像Javascript表达式,除了以下情况:
- 不能是赋值,如=、+=等
- 不能有new
- 不能用;或,连接多个表达式
- 不能是++、--
- 不能是位操作&、|
- 不能是表达式算符|、?.
模板表达式只能引用组件实例的成员,不能引用全局变量,包括window、document等。 编写模板表示式需注意以下问题:
- 没有可视的副作用:即表达式不能修改其他属性的状态,从而改变其他界面显示
- 执行很快:模板表达式可能在每次鼠标移动时被调用
- 简单:复杂的业务逻辑应该实现在组件中,而不是写成模板表达式
- 等幂性:即依赖值不变的话,模板表达式结果总是相同的。该特性该改进Angular变更检测的效率。
4. 模板语句 Template Statement
模板语句用于响应HTML元素、组件或Directive事件,语法如(event)=”statement”。
模板语句有类似模板表达式的语法限制。只能引用组件实例的成员,以及:
- 模板局部变量,如#item
- $event事件对象。
5. 绑定语法
数据绑定(Data Binding)是数据和视图自动保持一致的机制。
数据绑定按绑定方向分为以下三类:
- 数据 -> 视图:{{expression}}或[target] = "expression",绑定类型包括Interpolation、Property、Attribute
- 视图 -> 数据: (target) = "statement",绑定类型是Event
- 双向绑定: [(target)] = "expression",一般用于表单
Attribute和Property的区别是:Attribute指HTML Attribute,由HTML规范定义;Property指DOM Property, 由Document Obejct Model定义。一般地,Attribute值用于初始化Property值,Attribute值是不变的,Property值在被初始化后可以改变。
Attribute和Property间没有必然联系,例如:
- id既是Attribute又是Property
- colspan是Attribute,但没有对应的Property
- textContent是Property,但没有对应的Attribute。 注意,Angular 2数据绑定应用于Property和事件,而不是Attribute。
数据绑定按绑定类型分类如下:
- Property
- 元素Property,如
<img [src] = "heroImageUrl">
- 组件Property,如
<hero-detail [hero]="currentHero"></hero-detail>
- Directive Property,如
<div [ngClass] = "{selected: isSelected}"></div>
- 元素Property,如
- Event
- 元素事件,如
<button (click) = "onSave()">Save</button>
- 组件事件,如
<hero-detail (deleteRequest)="deleteHero()"></hero-detail>
- Directive事件,如
<input [(ngModel)]="heroName">
- 元素事件,如
- 双向:
- 事件和Property,如
<input [(ngModel)]="heroName">
- 事件和Property,如
- Attribute:
- 元素 Attribute,如
<button [attr.aria-label]="help">help</button>
- 元素 Attribute,如
- Class:
- 元素clsss Property,如
<div [class.special]="isSpecial">Special</div>
- 元素clsss Property,如
- Style:
- 元素sytle Property,如
<button [style.color] = "isSpecial ? 'red' : 'green'">
- 元素sytle Property,如
需要说明的是:
- Property绑定固定不变字符串时,[]可省略,如
<hero-detail prefix="You are my" [hero]="currentHero"></hero-detail>
- 插值和数据绑定均可以用于模板时,推荐使用插值
- 可以使用[class]绑定整个class
- 可以使用ngClass和ngStyle Directive同时设置多个class或style
- 可用于事件绑定的元素事件列表参见MDN
- 事件参数命名为$event,对于DOM元素该对象是标准的DOM event对象
- 双向绑定一般用于表单,相当于:
<input [ngModel]=" heroName " (ngModelChange)=" heroName =$event">
6. Built-in Directives
内置Directives包括:
- ngFor: 循环显示模板
- NgForTrackBy: 优化ngFor性能
- ngIf: 根据if条件确定是否包括模板,false则不生成DOM
- ngSwitch: 根据取值显示不同的模板
- ngClass: 设置多个class属性
- ngStyle: 设置多个style属性
ngFor、ngIf和ngSwtich的完整语法使用template元素,以ngIf说明如下:
<template [ngIf]="currentHero"> <hero-detail [hero]="currentHero"></hero-detail> </template>
简单起见可使用*ngIf如下:
<hero-detail *ngIf="currentHero" [hero]="currentHero"></hero-detail>
7. 模板局部变量
使用#定义模板局部变量,以便在模板中引用该变量。例如:
<input #phone> <button (click)="callPhone(phone.value)">Call</button>
一般地,ngFor总会使用到模板局部变量。例如:
<li *ngFor="#n of names"> 姓名是{{n}} </li>
注意,Angular 2 beta 17语法已改为:
<li *ngFor="let n of names"> 姓名是{{n}} </li>
8. 模板表达式算符
- Pipe:格式转换,如
<div>{{ title | uppercase }}</div>
- Elvis: 能处理null或undefined,如
The name is {{hero?.firstName}}