属性型指令用于改变一个 DOM 元素的外观或行为。
指令概览
在 Angular 中有三种类型的指令:
- 组件 — 拥有模板的指令
- 结构型指令 — 通过添加和移除DOM元素改变DOM布局的指令,如
NgFor
和NgIf
- 属性型指令 — 改变元素显示和行为的指令,例如
NgStyle
创建一个简单的属性型指令
属性型指令至少需要一个带有@Directive
装饰器的控制器类。该装饰器指定了一个用于标识属性的选择器。
编写指令代码
1 | import { Directive, ElementRef, Input } from '@angular/core'; |
Directive
提供@Directive
装饰器功能。ElementRef
注入到指令构造函数中。 这样代码可以访问 DOM 元素。Input
将数据从绑定表达式传达到指令中。
@Directive
装饰器需要一个 CSS 选择器,以便从模板中识别出关联到这个指令的 HTML。
Angular 会为每个匹配的元素创建一个指令控制器类的实例,并把 Angular 的ElementRef
和Renderer
注入进构造函数。 ElementRef
是一个服务,它赋予我们通过它的nativeElement
属性直接访问 DOM 元素的能力。Renderer服务允许通过代码设置元素的样式。
使用属性型指令
1 | <p myHighlight>Highlight me!</p> |
添加了一个import
语句来获得Highlight
指令类,并把这个类添加到 NgModule
元数据的declarations
数组中:
1 | import { NgModule } from '@angular/core'; |
响应用户引发的事件
可以通过标准的JavaScript方式手动给宿主 DOM 元素附加一个事件监听器。 但这种方法至少有三个问题:
1. 必须正确的书写事件监听器。
2. 当指令被销毁的时候,必须拆卸事件监听器,否则会导致内存泄露。
3. 必须直接和 DOM API 打交道,应该避免这样做。
在Angular中需要用先引入HostListener
,因为需要用到HostListener
装饰器添加两个事件处理器,它们会在鼠标进入或离开时进行响应,这些处理器委托给了一个辅助方法,它用于为DOM元素设置颜色,就是你在构造器中声明和初始化过的el
:
1 | import { Directive, ElementRef, HostListener, Input } from '@angular/core'; |
使用数据绑定向指令传递值
绑定到@Input属性
@Input
装饰器的数据流是从绑定表达式流向指令内部的。
1 | string; () myHighlight: |
1 | <p [myHighlight]="color">Highlight me!</p> |
1 | export class AppComponent { |
绑定到@Input别名
可以随意命名该指令的属性,并且给它指定一个用于绑定的别名:
1 | 'myHighlight') highlightColor: string; ( |
在指令内部,该属性叫highlightColor
,在外部,当我们绑定到它时,它叫myHighlight
。
绑定到第二个属性
把第二个名叫defaultColor
的输入属性添加到HighlightDirective
中:
1 | string; () defaultColor: |
修改该指令的onMouseEnter
,让它首先尝试使用highlightColor
进行高亮,然后用defaultColor
,如果它们都没有指定,那就用红色作为后备。
1 | 'mouseenter') onMouseEnter() { ( |
像组件一样,你也可以绑定到指令的很多属性,只要把它们依次写在模板中就行了:
1 | <p [myHighlight]="color" defaultColor="black"> |
Angular之所以知道defaultColor
绑定属于HighlightDirective
,是因为已经通过@Input
装饰器把它设置成了公共属性。
为什么要加@Input
@Input
装饰器都告诉Angular,该属性是公共的,并且能被父组件绑定。 如果没有@Input
,Angular就会拒绝绑定到该属性。
Angular把组件的模板看做从属于该组件的。 组件和它的模板默认会相互信任。 这也就是意味着,组件自己的模板可以绑定到组件的任意属性,无论是否使用了@Input
装饰器。
但组件或指令不应该盲目的信任其它组件或指令。 因此组件或指令的属性默认是不能被绑定的。 从Angular绑定机制的角度来看,它们是私有的,而当添加了@Input
时,它们变成了公共的 只有这样,它们才能被其它组件或属性绑定。
可以根据属性名在绑定中出现的位置来判定是否要加@Input
。
- 当属性名出现在等号右侧的模板表达式中时,它属于模板所在的组件,不需要
@Input
装饰器。 - 当属性名出现在等号左边的方括号([ ])中时,该属性属于其它组件或指令,它必须带有
@Input
装饰器。