Angularjs2 指令

在Angular1中,会经常使用到指令,主要作为一些功能组件的封装,通过module.directive(name, function){}来创建一个指令,然后通过restrict属性的ECMA来约束指令的使用位置,所以我们可以将一些通用的模块功能,封装为一个指令,然后在开发中直接使用指令就可完成相关功能的导入,而在Angularjs2中,指令的这个目标当然还是保持一致的。

在Angularjs2中,指令主要分为三种类型:

  1. 组件 - 带有模板的指令(组件皆指令)
  2. 结构型指令 - 作用于DOM元素修改视图结构的指令(NgIfNgFor等)
  3. 属性型指令 - 作用于DOM元素的外观和行为的指令(NgStyle等)

属性型指令
在前面基础语法中也提到过属性型指令,这里就依据教程简单的了解下自定义指令的写法,实现目标是:自定义实现一个可以修改元素内容以及样式的指令。

自定义属性型指令
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Directive, ElementRef, Renderer2, AfterViewInit} from '@angular/core';
//指令声明
@Directive({ selector: '[directiveDemo]' })
export class DemoDirective implements AfterViewInit{
constructor(
private el: ElementRef,
private render: Renderer2){}
ngAfterViewInit():void {
this.render.setProperty(this.el.nativeElement, "innerHTML", "Hi Directive!");
this.render.setStyle(this.el.nativeElement, "color", "red");
}
}

  • 通过Directive注解来声明为指令,并配置指令元数据。
  • 元数据中selector值为自定义的基于attribute的css选择器,用于通过元素的attribute来选择元素。
  • 通过注入ElementRef可以获取到当前元素的引用,可以使用nativeElement。
  • 通过注入Renderer2 可以使用统一的接口对当前元素进行访问和操作。由于Angular提倡的是一套框架,多种平台,所以建议使用统一的接口访问。
  • 实现生命周期中的AfterViewInit函数,可以是我们在指令初始化完成后完成一些操作。

我们在自定义指令中,通过Renderer2对象的setPropertysetStyle接口来完成innerHTML和style的color设置。

然后将指令添加到的@NgModuledeclarations中,然后在模板中使用指令:

使用自定义指令
1
<div directiveDemo>Demo</div>

运行结果将会看到一个红色Hi Directive!出现在界面上。

结构型指令
结构型指令其实就是会修改HTML的布局,一般使用*(星号)来标识。简单的实现一个结构型指令,且每个宿主上只能由一个结构型指令。这里使用官方的示例,类似NgIf

自定义结构型指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
//指令定义
@Directive({ selector: '[myUnless]'})
export class UnlessDirective{
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef){}
//input参数定义
@Input() set myUnless(condition: boolean){
if(condition){
this.viewContainer.createEmbeddedView(this.templateRef);
}else{
this.viewContainer.clear();
}
}
}

  • 通过注入TemplateRef来获取<ng-template>的内容。
  • 通过注入ViewContainerRef来访问视图容器。

然后将指令添加到的@NgModuledeclarations中,然后在模板中使用指令:

使用自定义指令
1
<div *myUnless="true">Demo</div>

运行结果后,可以通过修改myUnless的值来看到效果。

到这里,关于自定义指令的初级介绍也就完成了,当然在自定义指令中必不可少的数据和事件交互,以及自定义指令中那些个自动注入的对象的作用等,我们会在后面一个一个的进行了解学习。

指令的自定义事件

在指令中,我们可以通过nativeElement获取到原生元素,然后做各种操作,同时Angular2也提供了一个@HostListener(位于@angular/core包)来进行事件绑定,

HostListener绑定事件
1
2
3
4
5
6
7
8
@HostListener('mouseenter') onMouseEnter(){
//事件逻辑
this.render.setProperty(this.el.nativeElement, "innerHTML", "鼠标进入了!");
}
@HostListener('mouseleave') onMouseLeave(){
//事件逻辑
this.render.setProperty(this.el.nativeElement, "innerHTML", "鼠标离开了!");
}

将上段代码加入到我们开头写的属性型指令中,然后运行,鼠标移动到红色Hi Directive!上就可以看到事件被触发了。

结构型指令的内幕

在结构型指令中,在使用的时候都会提到*(星号),其实星号只是一个语法糖,经过Angular的处理最终都会编译成ng-template,可以结合官方文档的实例来分析下*ngIf的处理过程。

*ngIf
1
<div *ngIf="true">Demo!</div>

Angular会将*ngIf先解析为一个template属性,如下:

ngIf-template-attr
1
<div template="ngIf true">Demo!</div>

星号会被解析为一个template属性,然后将ngIf移动到属性值中。然后会继续解析template属性为:

ngIf-template
1
2
3
<ng-template [ngIf] = "true">
<div>Demo!</div>
</ng-template>

ngIf成为了ng-template的一个属性,而宿主元素则成为了ng-template的内容。而最终的展示效果为:

渲染结果
1
2
3
4
<!--bindings={
"ng-reflect-ng-if": "true"
}-->
<div _ngcontent-c0="">Demo!</div>

<ng-template>指令,是一个Angular元素,主要用来渲染HTML,只会以注释的方式来渲染。
<ng-container>指令,可以作为一个宿主元素使用,由于一个元素只能添加一个结构型指令,所以在一些需要多个结构型指令配合的情况下,如果使用HTML元素作为宿主元素,可能将会受到css样式的影响,这种情况下就推荐使用<ng-container>元素,该元素并不会被添加到DOM中。

参考链接

  1. Renderer2 API
  2. HostListener API