在angular中共有三种指令:
- 组件(特殊的指令)
- 结构指令,通过添加或移除DOM元素来更改DOM布局
- 属性指令,改变元素、组件或其他组件的外观或行为
属性指令修改一个元素的外观或行为。
创建属性指令
先创建指令文件:
1 | ng generate directive hero/ad |
在hero模块里面创建ad指令文件,创建的文件为:ad.directive.ts
,并且自动引入到模块文件里面:
1 | import { AdDirective } from './ad.directive'; |
可以看到在模块中导入了我们的AdDirective
指令,并在这模块的declarations
数组中引入了,复习下NgModule装饰器的declarations
元数据的作用:
declarations 声明本模块中拥有的视图类,angular有三种视图类:组件、指令、管道。
再看指令内容:
1 | import { Directive, ElementRef } from '@angular/core'; |
首先我们导入了Directive
符号和ElementRef
服务,Directive
提供了@Directive
指令装饰器函数,ElementRef
服务可以让我们通过它的nativeElement
属性直接访问DOM元素。
装饰器函数@Directive
以配置对象参数的形式,声明指令的元数据。
指令装饰器允许将一个类装饰为angular指令,并提供额外的元数据,以确定应该如何在运行时处理、实例化和使用指令。
一个指令必须属于一个NgModule才能被另一个指令、组件或应用程序使用,这就是为啥我们在前面引入指令的时候将指令添加到了NgModule的declarations数组中。
指令的元数据属性
指令的元数据属性挺丰富的,我就挑常用的几个就好了,后续用到再更新。
常用的指令元数据:
- selector:string 选择器
- inputs:string[] 输入属性
- outputs:string[] 输出属性
- host:{[key:string:string]} 监听宿主元素的dom事件
- exportAs:string 定义可以在模板中引用的名称,已将该指令分配给变量
selector:string 选择器
angular只允许指令触发不跨越元素边界的CSS选择器,选择器可以被的形式:
- element-name:按照元素名称选择,比如
<app-ad></app-ad>
- .class:按照类名选择,比如
<div class="app-ad"></div>
- [attribute]:按照属性名称选择,比如:
<div appAd></div>
- [attribute=value]:按照属性名和值进行选择,比如
<div appAd="test"></div>
- :not(sub_selector):仅当元素不匹配sub_selector时才选择
- selector1, select2:selector1或select2两者出现一个就选择
例如前面的AdDirective指令:
1 | @Directive({ |
1 | <h1 appAd>{{title}}</h1> |
inputs:string[]/outputs:string 输入属性/输出属性
标记输入类型的属性。
1 | @Directive({ |
但是建议是使用通过装饰器标记输入输出属性,因为这样更易读。
1 | @Directive({ |
输出属性和输入属性类似,只不过输入属性是从父组件到子组件,输出属性是从子组件到父组件。
host 宿主监听
定义与宿主元素相关的事件、操作、属性(attribute)、属性(property)。
Host Lestion
通过定义一个键值对方法来监听宿主元素的DOM事件:
- event:指令监听的DOM事件,至于有哪些监听DOM事件,可以看这个MDN
- statement:事件发生时候执行的命令,如果返回false,就将
preventDefault
应用于DOM事件
例如:
1 | host: { |
不过上面的这种写法是不推荐的,推荐使用装饰器函数@HostListener
:
1 | @HostListener('click') onClick(btn) { |
是不是很完美?然而,我们可以发现触发函数后,btn是undefined。💔
很绝望啊有木有!
我们不妨去看看装饰器@HostListener
:
1 | /** |
可以看到,出了eventName
,还有个args: string[]
,也就是参数数组,所以我们可以用数组的方式往装饰器函数里面传参数:
1 | @HostListener('click', ['$event.target', '$event.x']) onClick(btn: HTMLElement, x) { |
ok,完美。
再看看响应事件方法里面的reture false
有什么用。如果返回false,就将preventDefault
应用于DOM事件。那么什么是preventDefault
?
如果事件可取消,则取消该事件,而不停止事件的进一步传播。也就是取消事件的默认动作。该方法通知web浏览器不要执行与事件关联的默认动作(如果存在这样的动作)。
比如,一个复选框标签,点击后会选中,如果使用preventDefault
就可以在点击后无法选中复选框:
1 | <input id="test" type="checkbox" appAd value="1"/><label for="test">测试默认事件</label> |
我们将指令应用在一个复选框上,同时,指令用@HostListener
装饰器绑定的onClick
函数返回false
:
1 | @HostListener('click', ['$event.target', '$event.x']) onClick(btn: HTMLElement, x) { |
然后点击复选框的时候,发现click方法是正常的,但是点击无法选中复选框。
HostBinding
angular在更改检测期间自动检查绑定的宿主属性,如果绑定发生变化,那么它将更新指令的宿主元素。
HostBinding接受一个可选参数,指定被更新的主机元素的属性名称,如果未提供,就是用类属性名称,例如:
1 | <input type="text" [(ngModel)]="myHero.name" appAd /> |
1 | @HostBinding('class.valid') get valid() { |
第一个是给宿主元素绑定了类valid
,根据valid
的值来决定是否加上这个类。第二个绑定类似,第三个绑定是绑定了属性role
,通过role
值来绑定,这个值是写死的,不会改变。前两个值是通过输入框里面绑定的NgModel
的值来判断,在应用运行的过程中会动态监测。
exportAs
定义可以在模板中使用的名称,可以将指令通过模板引用的方式引用给变量。
1 | @Directive({ |
1 | <input type="text" [(ngModel)]="myHero.name" appAd #aa="aa"/> |