Angular复习笔记5-指令
@H_502_4@在Angular中,指令是一个重要的概念,它作用在特定的DOM元素上,可以扩展这个元素的功能,为元素增加新的行为。本质上,组件可以被理解为一种带有视图的指令。组件继承自指令,是指令的一个子类,通常被用来构造UI控件。 @H_502_4@指令的使用并不复杂,它与HTML元素属性的使用方式相似。不同的是,HTML语法标准为HTML元素预定义了特定的属性,浏览器遵循这一语法标准,实现了这些属性的内置行为。语法标准预定义的属性是有限的、不可扩展的,而Angular中的指令是可自定义的、可任意扩展的,这在一定程度上弥补了标准HTML元素属性功能的不足。指令分类
@H_502_4@在angular中指令分为三类:属性型指令,结构型指令和组件。属性型指令
@H_502_4@顾名思义,属性指令是以元素属性的形式来使用的指令。与HTML元素的内置属性不同,指令是Angular对HTML元素属性的扩展,浏览器本身不能识别这些指令,指令仅在Angular环境中才能被识别使用。属性指令通常被用来改变元素的外观和行为,如在第7章中介绍过的Angular内置指令NgStyle,它可以基于组件的状态来动态设置目标元素的样式。结构型指令
@H_502_4@结构指令可以用来改变DOM树的结构。结构指令可以根据模板表达式的值,增加或删除DOM元素,从而改变DOM的布局。结构指令与属性指令的使用方式相同,都是以元素属性的形式来使用的。两者的区别在于使用场景不同,属性指令用来改变元素的外观和行为,而结构指令用来改变DOM树的结构。以Angular内置的结构指令NgIf为例,使用NgIf指令需要为指令绑定一个表达式,当表达式值为true时,该DOM元素及其子元素被添加至DOM中;当表达式值为false时,元素从DOM中被移除。示例代码如下: @H_502_4@
组件
@H_502_4@组件继承自指令,它的代码结构与指令也是相似的,不同之处在于组件有一个自描述的模板,并且组件是用@Component来修饰,而指令需要一个宿主元素,用@Directive来描述。 @H_502_4@组件与指令的部分生命周期钩子函数相同: @H_502_4@自定义属性型指令
@H_502_4@一个属性指令需要一个控制器类,该控制器类使用@Directive装饰器来装饰。@Directive装饰器指定了用以标记指令所关联属性的选择器,控制器类则实现了指令所对应的特定行为。 @H_502_4@首先需要从@angular/core中引入Directive和ElementRef。前者包含了修饰指令的装饰器@Directive,后者通过指令的构造函数传入,代表指令修饰的DOM元素宿主。@Directive({ selector: '[appFirst]' }) export class FirstDirective implements OnInit { constructor(el: ElementRef) { } ngOnInit(): void { } }@H_502_4@上面的代码便是创建了一个指令,我们需要在@Directive中定义这个指令的选择器,选择器的规则遵循CSS选择器标准,例如上面的选择器是[appFirst],表示该指令会以属性的方式附着在元素上面。 @H_502_4@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'; } }
为指令绑定输入
@H_502_4@在上述的代码中指令为宿主设置的颜色是固定的,如果要想动态的变更颜色,有两个思路: @H_502_4@第一,设置一个@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) { } }@H_502_4@第二,指令实现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) { } }@H_502_4@然后在宿主中使用这个指令:
<p appFirst [backgroundColor]="color" matLine>template</p>@H_502_4@在宿主中需要定义一个color变量并且改变这个color变量的值,这里就不演示了。
响应用户操作
@H_502_4@可以使用@HostListener来响应用户发出的事件。@HostListener指向使用指令的DOM元素,使得DOM元素的事件与指令关联起来。@HostListener是另一种动态设置的方案,相比较前面那种@Input set输入属性的方式还有生命周期钩子的方式,这种方式是从响应用户发出的事件的角度执行的。//.....其他代码.... @HostListener('click') onClick(){ //.....代码逻辑.... }
自定义结构型指令
@H_502_4@不同于属性型指令,属性型指令会在构造函数中传入一个ElementRef来表示它的宿主DOM元素,而结构型指令需要在构造器中传入两个参数,一个是TempleteRef,另一个是ViewContainerRef.这是因为被结构型指令修饰的DOM元素会被angular转译成一个<ng-temeplate>元素:<span *ngIf="condition">can you see it?</span>@H_502_4@上面的代码会被angular转译为:
<ng-temeplate [ngIf]="condition"> <span>can you see it?</span> </ng-template>@H_502_4@可以看出结构型指令会被转译成一个属性型的指令。 @H_502_4@所以,结构型指令需要传入一个TemeplateRef的服务来访问到这个组件模板(被转译成的ng-template)。至于另一个ViewContainerRef,这个服务是可以从DOM中创建或者删除一个TemplateRef所代表的模板元素,要取决于对结构型指令求值的boolean值(因为给结构型指令绑定的表达式必须是一个boolean类型的表达式)。 @H_502_4@下面代码简单定义了一个结构型的指令:
@Directive({ selector: '[appFirst]' }) export class FirstDirective { constructor(templ: TemplateRef<any>, container: ViewContainerRef) { } @input() condition: boolean; }@H_502_4@看起来和属性型的指令差不多,不同之处在于构造函数传入的参数类型不同。使用上也和属性型指令差不多,需要注意的就是ViewContainerRef的两个方法,这两个方法都是用来操作指令中的TemplateRef所指向的模板的: @H_502_4@createEmbeddedView():需要传入一个TemplateRef类型的参数,表示根据TemplateRef创建一个内嵌的视图模板。 @H_502_4@clear():将TemplateRef视图模板中删除。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。