Angular复习笔记5-指令

在Angular中,指令是一个重要的概念,它作用在特定的DOM元素上,可以扩展这个元素的功能,为元素增加新的行为。本质上,组件可以被理解为一种带有视图的指令。组件继承自指令,是指令的一个子类,通常被用来构造UI控件。

指令的使用并不复杂,它与HTML元素属性的使用方式相似。不同的是,HTML语法标准为HTML元素预定义了特定的属性,浏览器遵循这一语法标准,实现了这些属性的内置行为。语法标准预定义的属性是有限的、不可扩展的,而Angular中的指令是可自定义的、可任意扩展的,这在一定程度上弥补了标准HTML元素属性功能的不足。

指令分类

在angular中指令分为三类:属性型指令,结构型指令和组件。

属性型指令

顾名思义,属性指令是以元素属性的形式来使用的指令。与HTML元素的内置属性不同,指令是Angular对HTML元素属性的扩展,浏览器本身不能识别这些指令,指令仅在Angular环境中才能被识别使用。属性指令通常被用来改变元素的外观和行为,如在第7章中介绍过的Angular内置指令NgStyle,它可以基于组件的状态来动态设置目标元素的样式。

结构型指令

结构指令可以用来改变DOM树的结构。结构指令可以根据模板表达式的值,增加或删除DOM元素,从而改变DOM的布局。结构指令与属性指令的使用方式相同,都是以元素属性的形式来使用的。两者的区别在于使用场景不同,属性指令用来改变元素的外观和行为,而结构指令用来改变DOM树的结构。
以Angular内置的结构指令NgIf为例,使用NgIf指令需要为指令绑定一个表达式,当表达式值为true时,该DOM元素及其子元素被添加至DOM中;当表达式值为false时,元素从DOM中被移除。示例代码如下:

上面的代码当condition的值为true时会显示p元素,反之p元素会从DOM中移除。

组件

组件继承自指令,它的代码结构与指令也是相似的,不同之处在于组件有一个自描述的模板,并且组件是用@Component来修饰,而指令需要一个宿主元素,用@Directive来描述。

组件与指令的部分生命周期钩子函数相同:

尽管组件与指令有相似的结构和一些相同的生命周期钩子方法,但是它们也有一些不同点。不同于属性指令和结构指令,组件不是以HTML元素属性的形式使用的,而是以自定义标签的形式使用的,原因在于组件带有模板。组件可作为对HTML元素的扩展,将自身的模板视图插入DOM中;而属性指令和结构指令是对HTML元素属性的扩展,其作用是扩展已有DOM元素的行为和样式,或者改变这些元素在DOM中的结构。

自定义属性型指令

一个属性指令需要一个控制器类,该控制器类使用@Directive装饰器来装饰。@Directive装饰器指定了用以标记指令所关联属性的选择器,控制器类则实现了指令所对应的特定行为。

首先需要从@angular/core中引入Directive和ElementRef。前者包含了修饰指令的装饰器@Directive,后者通过指令的构造函数传入,代表指令修饰的DOM元素宿主。

@Directive({
selector: '[appFirst]'
})
export class FirstDirective implements OnInit {
constructor(el: ElementRef) { }
ngOnInit(): void { } }

上面的代码便是创建了一个指令,我们需要在@Directive中定义这个指令的选择器,选择器的规则遵循CSS选择器标准,例如上面的选择器是[appFirst],表示该指令会以属性的方式附着在元素上面。

Angular会为每一个匹配的DOM元素创建一个指令实例,同时将ElementRef作为参数注入到控制器构造函数。使用ElementRef服务,可以在代码中通过其nativeElement属性直接访问DOM元素,这样就可以通过DOM API设置元素的背景色:

@Directive({
selector: '[appFirst]'
})
export class FirstDirective implements OnInit {
constructor(private el: ElementRef) { }
ngOnInit(): void {
console.log(this.el);
this.el.nativeElement.style.backgroundColor = 'red';
} }

为指令绑定输入

在上述的代码中指令为宿主设置的颜色是固定的,如果要想动态的变更颜色,有两个思路:

第一,设置一个@Input输入属性,并将这个属性定义为set函数:

@Directive({
selector: '[appFirst]'
})
export class FirstDirective {
@Input() set backgroundColor(color: string) {
if (color) {
this.el.nativeElement.style.backgroundColor = color;
}
}
constructor(private el: ElementRef) { } }

第二,指令实现onChanges接口,在接口中处理逻辑:

@Directive({
selector: '[appFirst]'
})
export class FirstDirective implements OnChanges {
@Input() backgroundColor: string;
ngOnChanges(changes: SimpleChanges): void {
this.el.nativeElement.style.backgroundColor = this.backgroundColor;
}
constructor(private el: ElementRef) { } }

然后在宿主中使用这个指令:

 <p appFirst [backgroundColor]="color" matLine>template</p>

在宿主中需要定义一个color变量并且改变这个color变量的值,这里就不演示了。

响应用户操作

可以使用@HostListener来响应用户发出的事件。@HostListener指向使用指令的DOM元素,使得DOM元素的事件与指令关联起来。@HostListener是另一种动态设置的方案,相比较前面那种@Input set输入属性的方式还有生命周期钩子的方式,这种方式是从响应用户发出的事件的角度执行的。

//.....其他代码....
@HostListener('click')
onClick(){
//.....代码逻辑....
}

自定义结构型指令

不同于属性型指令,属性型指令会在构造函数中传入一个ElementRef来表示它的宿主DOM元素,而结构型指令需要在构造器中传入两个参数,一个是TempleteRef,另一个是ViewContainerRef.这是因为被结构型指令修饰的DOM元素会被angular转译成一个<ng-temeplate>元素:

<span *ngIf="condition">can you see it?</span>

上面的代码会被angular转译为:

<ng-temeplate [ngIf]="condition">
<span>can you see it?</span>
</ng-template>

可以看出结构型指令会被转译成一个属性型的指令。

所以,结构型指令需要传入一个TemeplateRef的服务来访问到这个组件模板(被转译成的ng-template)。至于另一个ViewContainerRef,这个服务是可以从DOM中创建或者删除一个TemplateRef所代表的模板元素,要取决于对结构型指令求值的boolean值(因为给结构型指令绑定的表达式必须是一个boolean类型的表达式)。

下面代码简单定义了一个结构型的指令:

@Directive({
selector: '[appFirst]'
})
export class FirstDirective {
constructor(templ: TemplateRef<any>, container: ViewContainerRef) { }
@Input() condition: boolean;
}

看起来和属性型的指令差不多,不同之处在于构造函数传入的参数类型不同。使用上也和属性型指令差不多,需要注意的就是ViewContainerRef的两个方法,这两个方法都是用来操作指令中的TemplateRef所指向的模板的:

createEmbeddedView():需要传入一个TemplateRef类型的参数,表示根据TemplateRef创建一个内嵌的视图模板。

clear():将TemplateRef视图模板中删除。

Angular复习笔记5-指令的更多相关文章

  1. Angular复习笔记7-路由(下)

    Angular复习笔记7-路由(下) 这是angular路由的第二篇,也是最后一篇.继续上一章的内容 路由跳转 Web应用中的页面跳转,指的是应用响应某个事件,从一个页面跳转到另一个页面的行为.对于使 ...

  2. Angular复习笔记7-路由(上)

    Angular复习笔记7-路由(上) 关于Angular路由的部分将分为上下两篇来介绍.这是第一篇. 概述 路由所要解决的核心问题是通过建立URL和页面的对应关系,使得不同的页面可以用不同的URL来表 ...

  3. angular复习笔记4-模板

    Angular复习笔记4-模板 简介 模板是一种自定义的标准化页面,通过模板和模板中的数据结合,可以生成各种各样的网页.在Angular中,模板的默认语言是HTML,几乎所有的HTML语法在模板中都是 ...

  4. Angular复习笔记6-依赖注入

    Angular复习笔记6-依赖注入 依赖注入(DependencyInjection)是Angular实现重要功能的一种设计模式.一个大型应用的开发通常会涉及很多组件和服务,这些组件和服务之间有着错综 ...

  5. angular复习笔记1-开篇

    前言 学习和使用angular已经有一段时间了.这段时间利用angular做了一个系统,算是对angular有了一个全面的认识,趁着现在有一些时间,把angular的一些知识记录一下. 安装angul ...

  6. angular复习笔记3-组件

    组件Component 组件是构成angular应用的核心,angular的有序运行依赖于组件的协同工作,组件之于angular应用就像是汽车和汽车零部件的意思. 概述 近几年的前端发展迅速,各种工程 ...

  7. angular复习笔记2-架构总览

    angular架构总览 一个完整的Angular应用主要由6个重要部分构成,分别是:组件.模板.指令.服务.依赖注入和路由.这些组成部分各司其职,而又紧密协作,它们的关系如图所示. 与用户直接交互的是 ...

  8. Angular 学习笔记——自定义指令之间的交互

    <!DOCTYPE html> <html lang="en" ng-app="myApp"> <head> <met ...

  9. Angular 学习笔记——自定义指令

    <!DOCTYPE HTML> <html ng-app="myApp"> <head> <meta http-equiv="C ...

随机推荐

  1. 解决IDEA中maven导入jar包

    查      看:   File------>Project Structure--------->Libraries如下面没有maven所引入的jar包则为该错误[1] 错误原因:  是 ...

  2. sql server 使用链接服务器远程查询

    --PKselect * from sys.key_constraints where object_id = OBJECT_ID('TB')--FKselect * from sys.foreign ...

  3. 自动创建Kibana索引

    参考 https://www.cnblogs.com/dance-walter/p/10471950.html 参考 https://www.elastic.co/guide/en/kibana/cu ...

  4. 使用ssh-keygen生成私钥和公钥

    1.使用ssh-keygen生成私钥和公钥 命令如下: ssh-keygen -t rsassh-keygen -t rsa -C "用户名自取"可以是邮箱 例子: fdipzon ...

  5. Linux的tmpfs和ramfs

    tmpfs tmpfs是一种虚拟内存文件系统, 它的存储空间在VM里面,现在大多数操作系统都采用了虚拟内存管理机制, VM(Virtual Memory) 是由Linux内核里面的VM子系统管理. V ...

  6. Oracle 如何恢复删除并提交的表数据

    在Oracle的数据库中,如果不小心删除数据,该如何恢复数据呢? 有两种方法 :scn 方法和时间戳方法 一.恢复删除数据的SQL语法(建议用时间戳) 1.通过scn恢复删除且已提交的数据 1)获得当 ...

  7. OSPF协议介绍及配置

    一.OSPF概述 回顾一下距离矢量路由协议的工作原理:运行距离矢量路由协议的路由器周期性的泛洪自己的路由表,通过路由的交互,每台路由器都从相邻的路由器学习到路由,并且加载进自己的路由表中,而对于这个网 ...

  8. pytesseract.pytesseract.TesseractNotFoundError: tesseract is not installed or it's not in your path

    使用pytesseract识别验证码中遇到异常如下: pytesseract.pytesseract.TesseractNotFoundError: tesseract is not installe ...

  9. Python - importlib 模块

    importlib 模块可以根据字符串来导入相应的模块 目录结构: 在根目录下创建 importlib_test.py 和 aaa/bbb.py bbb.py: class Person(object ...

  10. spring boot使用AOP统一处理web请求

    为了保证服务的高可用,及时发现问题,迅速解决问题,为应用添加log是必不可少的. 但是随着项目的增大,方法增多,每个方法加单独加日志处理会有很多冗余 那在SpringBoot项目中如何统一的处理Web ...