属性型指令能够改变一个元素、组件或其他指令的外观和行为。它和结构型指令不同,结构型指令能够添加或删除某一元素,以此改变DOM结构,也就是说,属性型指令可以改变自身,而结构型指令可以改变外界。比如说,你可以控制自己的行为,改变自己的容貌,整理自己的着装,但你无法控制自己的出生和死亡。
接下来,我们来创建一个简单的属性型指令appHighlight ,当用户把把鼠标停留在一个元素上时,改变它的背景色。
在命令行窗口用cli命令创建指令类文件,
ng generate directive highlight
生成的 src/app/highlight.directive.ts 文件如下:
import { Directive } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor() { }
}
从生成的文件中我们可以看出,属性型指令需要一个带@Directive装饰器修饰的类来支持指令需要的行为,该装饰器指定了一个用于标识属性的选择器。
@Directive装饰器中配置了该指令的属性型选择器[appHighlight],方括号表示选择器,Angular会在模板中定位每个拥有appHighlight属性的元素,并为这些元素添加本指令的逻辑。
装饰器下的类就是指令的控制器类,接下来我们要在控制器类中添加逻辑,并将其导出以便能在其它地方使用。
import { Directive, ElementRef } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = 'yellow';
}
}
import语句从Angular的core库中导入了ElementRef类,该类用于包装某个原生元素,该类的属性nativeElement就是背后的原生元素,并且在构造函数中注入了宿主DOM元素的引用,所以我们有了直接访问宿主DOM元素的能力。
要想使用这个新的HighlightDirective,就往根组件的AppComponent 的模板中添加一个 p元素,并把该指令作为一个属性使用。
<p appHighlight>Highlight me!</p>
总结,Angular在宿主元素p上发现了一个appHighlight属性,然后它创建了一个该属性所在类HighlightDirective的实例,并将宿主元素p的引用注入到构造函数中,进行初始化,所以p元素背景色被设置成黄色。
响应用户引发的事件:
我们需要在鼠标悬浮在一个元素上时改变它的颜色,鼠标离开时复原,因此我们就要添加事件处理器。
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) { }
@HostListener('mouseenter') onMouseEnter() {
this.highlight('yellow');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}
@HostListener装饰器让你订阅宿主元素的事件,mouseenter、mouseleave是angular的内置事件,和click是一样的,表示鼠标键入和离开事件,而onMouseEnter、onMouseLeave则是自定义方法的名字,由于现在背景颜色是反复变化的,因此便不能直接在构造函数中初始化,我们单独抽取出highlight方法,让onMouseEnter、onMouseLeave方法分别去调用。
使用 @Input 数据绑定向指令传递值
上述的元素颜色设置是在底层直接写死的,这样很不方便,我们希望能在外部直接设置颜色,而不需要更改底层代码。
指令类highlight.directive.ts:
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) { }
@Input('appHighlight') highlightColor: string;
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || 'red');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}
app.component.ts:
export class AppComponent {
color = 'yellow';
}
app.component.html:
<p [appHighlight]="color">Highlight me!</p>
我们在指令类中引入@Input装饰器,设置输入属性highlightColor,并指定别名appHighlight,在外部设置color属性,并将color属性绑定到输入属性,以便将值传入。你也可以不设置color属性,直接把颜色赋给输入属性:appHighlight=“yellow”,但是这样不够灵活,因为如果不设置对外变量,其它指令或元素将无法与它合作。鼠标键入方法中的red目的是如果有人忘记绑定highlightColor,就将其置为红色。
绑定到第二个属性:
真实的应用中应该有多个定制属性,在本例中,我们应该设置默认属性,先尝试用highlightColor ,再用defaultColor ,如果都没设置,再用red作为后备。
@Input() defaultColor: string;
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || this.defaultColor || 'red');
}
app.component.html:
<p [appHighlight]="color" defaultColor="violet">
Highlight me too!
</p>
注意,由于defaultColor属性绑定的是字符串常量,没有模板表达式需要计算,所以不需要加中括号。