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. UDS的使用

    我们通过对导热微分方程式的求解,并与Fluent自己的求解结果进行对比,介绍一下Fluent当中UDS(自定义标量)的具体使用方法. 首先Fluent当中的UDS主要针对下面这样形式的方程: 其中: ...

  2. [基础不过关填坑] 跨iframe触发事件

    子iframe $("#testId").on("change",function(){ alert("change") }) 父页面 $( ...

  3. 关于js函数解释(包括内嵌,对象等)

    常用写法: function add(a,b) { return a + b; } alert(add(1,2)); // 结果 3 当我们这么定义函数的时候,函数内容会被编译(但不会立即执行,除非我 ...

  4. Linux上tomcat部署web项目,访问报错"ip访问时间过长"

    项目原来是可以访问的,后来突然不能不访问了,系统ip也是能ping通的,后来就想是不是防火墙的问题,查看一下还真是 原因:Linux服务器上的防火墙开着,关闭即可 1.查看firewall服务状态 s ...

  5. git修改提交作者和邮箱

    作用一名程序员,我们会经常混迹与不同的代码仓库,时常不同仓库会有作者信息验证.比如公司内建的gitlab一般会要求统一使用公司内部的域账号签名:github要求使用github账号签名等.因此,很容易 ...

  6. python快速搭建http服务

    在Windows 7/10或Ubuntu上可以通过python2.x或python3.x来快速搭建一个简单的HTTP服务器. 如果python为2.x,则可执行:$ python -m SimpleH ...

  7. Redis (error) NOAUTH Authentication required.

    首先查看redis设置密码没 127.0.0.1:6379> config get requirepass 1) "requirepass" 2) "" ...

  8. (转nginx不浏览直接下载文件的解决方法

    原文:https://www.zhan200.com/xwt/39.html 如果nginx配置不对,就会造成部分文件,在浏览器中不是直接预览,而是进行了下载.修改的方法是修改配置文件.具体解决方法如 ...

  9. Java12新特性 -- 默认生成类数据共享(CDS)归档文件

    默认生成类数据共享(CDS)归档文件 同一个物理机/虚拟机上启动多个JVM时,如果每个虚拟机都单独装载自己需要的所有类,启动成本和内 存占用是比较高的.所以Java团队引入了类数据共享机制 (Clas ...

  10. C++ vector使用实例

    C++ vector #include <iostream> #include <vector> #include <string> #include <al ...