# 模板语法 使用模型-视图-控制器 (MVC) 概念来理解 Angular,组件扮演着控制器或视图模型的角色,模板则扮演视图的角色。 ## HTML 几乎所有的 HTML 语法都是有效的模板语法。但 `Syntax'; ``` 幸运的是,Angular 数据绑定对危险的 HTML 早有防备。在显示它们之前,会先对内容进行无害化处理。 ## HTML 属性、class 和 style 绑定 模板语法为那些不太适合使用属性绑定的场景提供了专门的单向数据绑定形式。 ### Attribute 绑定 我们可以通过 Attribute 绑定 来直接设置 Attribute 的值。 ARIA, SVG 和 table 中的 `colspan/rowspan` 等 Attribute。它们是纯粹的 Attribute,没有对应的属性可供绑定。 ```html One-Two ``` ### CSS 类绑定 借助 CSS 类绑定,我们可以从元素的 class Attribute 上添加和移除 CSS 类名。 CSS 类绑定在语法上类似于属性绑定,完整格式为 `[class.class-name]` 其中后面的`.class-name` 是可选的。 ```html
Bad curly special
Bad curly
This one is not so special
``` > 虽然这是一个切换单一类名的好办法,但我们通常更喜欢使用 `NgClass` 指令来同时管理多个类名。 ### 样式绑定 通过 样式绑定 ,我们可以设置内联样式。完整格式为 `[style.style-property]` 其中后面的 `.style-property` 是可选的。 ```html ``` > 虽然这是一个设置单一样式的好办法,但我们通常更喜欢使用 NgStyle 指令 来同时设置多个内联样式。 > > 注意,一个 样式属性 命名方法可以用 **中线命名法**,像上面的一样 也可以用 **驼峰式命名法**,比如 fontSize。 ## 事件绑定 事件绑定语法由等号左侧带圆括号的 **目标事件**,和右侧一个引号中的 **模板语句** 组成。下列事件绑定监听按钮的点击事件。无论什么时候,发生点击时,都会调用组件的 onSave() 方法。 ```html ``` ### 目标事件 圆括号中的名称 ——如 (click) ——标记出了目标事件。 **元素事件** 可能是更常见的目标,但 Angular 会先看这个名字是否能匹配上已知指令的事件属性。 如果这个名字没能匹配到元素事件或已知指令的输出型属性,Angular 就会报“未知指令”错误。 ### `$event` 和事件处理语句 在事件绑定中,Angular 会为目标事件设置事件处理器。当事件发生时,这个处理器会执行模板语句。 事件绑定会通过一个 **名叫 $event 的事件对象** 传达关于此事件的信息(包括数据值)。 事件对象的形态取决于目标事件。如果目标事件是一个原生 DOM 元素事件, $event 就是一个 DOM 事件对象 ,它有像 target 和 target.value 这样的属性。 如果这个事件属于指令(回想一下:组件是指令的一种),那么 $event 便具有指令中生成的形态。 ### 使用 EventEmitter 实现自定义事件 指令使用典型的 Angular `EventEmitter` 来触发自定义事件。指令调用 `EventEmitter.emit(payload)` 来触发事件,传进去的消息载荷可以是任何东西。父指令通过绑定到这个属性来监听这个事件,并且通过 $event 对象来访问这个载荷。 ```ts // HeroDetailComponent.ts template: `` // ... @Output() deleteRequest = new EventEmitter(); delete() { this.deleteRequest.emit(this.hero); } ``` ### 模板语句有副作用 deleteHero 方法有一个副作用:它删除了一个英雄。模板语句的副作用不仅没问题,反而正是我们所期待的。 ## 双向数据绑定 > 要使用 ngModel 做双向数据绑定,得先把 FormsModule 导入我们的模块并把它加入 NgModule 装饰器的 imports 数组。 ### `[(ngModel)]` 内幕 ```html ``` Angular 会把 `[(x)]` 语法去掉语法糖,变成了一个供属性绑定用的输入属性 x ,和一个供事件绑定用的输出属性 xChange。Angular 通过在模板表达式的原始字符串后面追加上 =$event,来构建出供事件绑定用的模板语句。 利用这一行为,我们也可以自己写出具有双向绑定功能的指令。 ```ts [(x)]="e" <==> [x]="e" (xChange)="e=$event" ``` ## 内置指令 前一个版本的 Angular 中包含了超过 70 个内置指令。社区贡献了更多,这还没算为内部应用而创建的无数私有指令。 在 Angular2 中我们不需要那么多指令。使用更强大、更富有表现力的 Angular 绑定系统,我们其实可以达到同样的效果。 我们仍然可以从简化复杂任务的指令中获益。Angular 发布时仍然带有内置指令,只是没那么多了。我们仍会写自己的指令,只是没那么多了。下面这部分就检阅一下那些最常用的内置指令。 ### `NgClass` CSS 类绑定 是添加或删除 单个 类的最佳途径。当我们想要同时添加或移除 多个 CSS 类时,NgClass 指令可能是更好的选择。 ```html
This div is saveable and special
``` ### `NgStyle` 我们可以基于组件的状态动态设置内联样式。 绑定到 NgStyle 可以让我们同时设置很多内联样式。 样式绑定 是设置 单一 样式值的简单方式,如果我们要同时设置 多个 内联样式, NgStyle 指令可能是更好的选择。 ```html ... ``` ### `NgIf` 通过把 NgIf 指令绑定到一个真值表达式,我们可以把一个元素的子树 ( 元素及其子元素 ) 添加到 DOM 上。 ```html
Hello, {{currentHero.firstName}}
``` #### 可见性和 NgIf 不是一回事 我们可以通过 类绑定 或 样式绑定 来显示和隐藏一个元素的子树(元素及其子元素)。隐藏一个子树和用 NgIf 排除一个子树是截然不同的。 当我们隐藏一个子树时,它仍然留在 DOM 中。子树中的组件及其状态仍然保留着。即使对于不可见属性,Angular 也会继续检查变更。子树可能占用相当可观的内存和运算资源。 当 NgIf 为 false 时,Angular 从 DOM 中实际移除了这个元素的子树。它销毁了子树中的组件及其状态,也潜在释放了可观的资源,最终让用户体验到更好的性能。 ### `NgSwitch` 当需要从 **一组** 可能的元素树中根据条件显示 **一个** 时,我们就把它绑定到 `NgSwitch`。Angular 将只把选中的元素树放进 DOM 中。 ```html Eenie Meanie other ``` ### `NgFor` ```html
{{hero.fullName}}
``` 赋值给 `*ngFor` 的字符串并不是一个 **模板表达式**,它是一个 **微语法** ——由 Angular 自己解释的小型语言。 `ngFor` 指令支持一个可选的 `index` ,它在迭代过程中会从 0 增长到“当前数组的长度”。 ```
{{i + 1}} - {{hero.fullName}}
``` `ngFor` 指令有时候会性能较差,特别是在大型列表中。对一个条目的一点小更改、移除或添加,都会导致级联的 DOM 操作。 如果我们给它一个 **追踪** 函数,Angular 就可以避免这种折腾。追踪函数告诉 Angular:我们知道两个具有相同 hero.id 的对象其实是同一个英雄。 ```ts trackByHeroes(index: number, hero: Hero) { return hero.id; } ``` ```html
({{hero.id}}) {{hero.fullName}}
``` ## `*` 与 `