5 Directive 指令
指令是Angular提供的操作DOM的途径. 指令分为属性指令和结构指令
Structural Directives (结构指令): 增加, 删除DOM节点以修改布局, 使用*作为指令前缀
Attribute Directives (属性指令): 修改现有元素的外观或行为, 使用[]包裹
5.1 Build-in Directives
5.1.1 *ngIf
Structural Directives
根据条件渲染DOM节点或移除DOM节点
<div *ngIf="data.length > 0; then dataList else noData"></div>
<ng-template #dataList>课程列表</ng-template>
<ng-template #noData>没有更多数据</ng-template>
app.component.ts
import { Component } from '@angular/core';
import { Form, FormControl, FormGroup, NgForm } from '@angular/forms';
interface List {
id: number
name: string
age: number
}
// 组件类 被@Component装饰器装饰
@Component({
// 制定组件的使用方式, 当前问标记形式
// app-root => <app-root></app-root>
selector: 'app-root', // 当前组件调用的时候你要以什么形式去调用
templateUrl: './app.component.html', // 当前组件对应的模版是什么 组件模版文件路径
styleUrls: ['./app.component.css'], // 当前组件对应的样式文件
})
// 导出一个类
export class AppComponent {
list: List[] = [
{
id: 1,
name: 'sam',
age: 20
},
{
id: 2,
name: 'jo',
age: 30
}
]
}
app.component.html
:
app.component.html
:
<div *ngIf="list.length === 0; then noData"></div>
<ng-template #noData>
<div>no content</div>
</ng-template>
<!-- #这个是模板引用变量,可以在模板中引用这个变量 -->
<!-- ng-template是一个模板容器,可以用来包裹一段html代码,然后通过ngIf来控制这段html代码的显示与隐藏 -->
<!-- *ngIf="list.length === 0; then noData" 这个语法是ngIf的语法糖,它的意思是当list.length === 0时,显示noData这个模板
也就是说当list为空时,显示noData这个模板,否则不显示
如果<div> 在ng-template 里, 则会显示<div>no content</div>,否则会显示no content
-->
5.1.2 [hidden] Attribute Directives
根据条件显示 DOM 节点或隐藏 DOM 节点(display)
5.1.3 *ngFor
遍历数据生成HTML结构
interface List {
id: number
name: string
age: number
}
list: List[] = [
{
id: 1,
name: 'sam',
age: 20
},
{
id: 2,
name: 'jo',
age: 30
}
]
app.component.ts
import { Component } from '@angular/core';
import { Form, FormControl, FormGroup, NgForm } from '@angular/forms';
interface List {
id: number
name: string
age: number
}
// 组件类 被@Component装饰器装饰
@Component({
// 制定组件的使用方式, 当前问标记形式
// app-root => <app-root></app-root>
selector: 'app-root', // 当前组件调用的时候你要以什么形式去调用
templateUrl: './app.component.html', // 当前组件对应的模版是什么 组件模版文件路径
styleUrls: ['./app.component.css'], // 当前组件对应的样式文件
})
// 导出一个类
export class AppComponent {
list: List[] = [
{
id: 1,
name: 'sam',
age: 20
},
{
id: 2,
name: 'jo',
age: 30
}
]
}
// selector: '.app-root', // 当前组件调用的时候你要以什么形式去调用
// app-root => <div class="app-root"></div>
// selector: '[app-root]', // 当前组件调用的时候你要以什么形式去调用
// app-root => <div app-root></div>
app.component.html
<div *ngIf="list.length === 0; then noData; else listData"></div>
<ng-template #noData>
<div>no content</div>
</ng-template>
<ng-template #listData>
<ul>
<li *ngFor="let item of list ">
<p>
{{ item.id }}
{{ item.name }}
{{ item.age }}
</p>
</li>
</ul>
</ng-template>
app.component.ts
import { Component } from '@angular/core';
import { Form, FormControl, FormGroup, NgForm } from '@angular/forms';
interface List {
id: number
name: string
age: number
}
// 组件类 被@Component装饰器装饰
@Component({
// 制定组件的使用方式, 当前问标记形式
// app-root => <app-root></app-root>
selector: 'app-root', // 当前组件调用的时候你要以什么形式去调用
templateUrl: './app.component.html', // 当前组件对应的模版是什么 组件模版文件路径
styleUrls: ['./app.component.css'], // 当前组件对应的样式文件
})
// 导出一个类
export class AppComponent {
list: List[] = [
{
id: 1,
name: 'sam',
age: 20
},
{
id: 2,
name: 'jo',
age: 30
}
]
identity(index: number, item: List) {
console.log(index);
console.log(item);
return item.id;
}
}
// selector: '.app-root', // 当前组件调用的时候你要以什么形式去调用
// app-root => <div class="app-root"></div>
// selector: '[app-root]', // 当前组件调用的时候你要以什么形式去调用
// app-root => <div app-root></div>
app.component.html
:
<div *ngIf="list.length === 0; then noData; else listData"></div>
<ng-template #noData>
<div>no content</div>
</ng-template>
<ng-template #listData>
<ul>
<li *ngFor="
let item of list;
let i = index;
let isFirst = first;
let isLast = last;
let isEven = even;
let isOdd = odd;
trackBy: identity;
" [ngClass]="{ even: isEven, odd: isOdd }">
<p>
{{ item.id }}
{{ item.name }}
{{ item.age }}
index {{ i }}
isFirst {{ isFirst }}
isLast {{ isLast }}
</p>
</li>
</ul>
</ng-template>
<!-- ngClass是angular提供的一个指令,用于动态添加或删除class -->
<!-- [ngClass]="{ even: isEven, odd: isOdd }" 是说如果isEven为true,则添加even这个class,如果isOdd为true,则添加odd这个class -->
<!-- .even {
background: pink;
}
.odd {
background: lightblue;
}
是说如果isEven为true,则添加even这个class,如果isOdd为true,则添加odd这个class
Angular 会自动帮我们添加这个class到对应的元素上 -->
app.component.css
:
5.2 Custom Directives
需求: 为元素设置默认背景颜色, 鼠标移入时的背景颜色以及移出时的背景颜色.
ng g d directives/hover
import { AfterViewInit, Directive, ElementRef } from '@angular/core';
import { After } from 'v8';
// @Directive是个装饰器, 用来告诉Angular这是一个指令
@Directive({
selector: '[appHover]'
})
export class HoverDirective implements AfterViewInit {
constructor(private elementRef: ElementRef) {
//elementRef是一个指向宿主元素的引用
//ElementRef是一个包装器,它包装了一个原生的DOM元素,可以通过nativeElement属性访问它
// console.log(elementRef);
}
ngAfterViewInit(){
console.log(this.elementRef.nativeElement);
}
}
import { AfterViewInit, Directive, ElementRef } from '@angular/core';
import { After } from 'v8';
// @Directive是个装饰器, 用来告诉Angular这是一个指令
@Directive({
selector: '[appHover]'
})
export class HoverDirective implements AfterViewInit {
element: Element
constructor(private elementRef: ElementRef) {
//elementRef是一个指向宿主元素的引用
//ElementRef是一个包装器,它包装了一个原生的DOM元素,可以通过nativeElement属性访问它
// console.log(elementRef);
this.element = elementRef.nativeElement;
}
ngAfterViewInit(){
console.log(this.element);
}
}
import { AfterViewInit, Directive, ElementRef } from '@angular/core';
import { After } from 'v8';
// @Directive是个装饰器, 用来告诉Angular这是一个指令
@Directive({
selector: '[appHover]'
})
export class HoverDirective implements AfterViewInit {
element: HTMLElement
constructor(private elementRef: ElementRef) {
//elementRef是一个指向宿主元素的引用
//ElementRef是一个包装器,它包装了一个原生的DOM元素,可以通过nativeElement属性访问它
// console.log(elementRef);
this.element = elementRef.nativeElement;
}
ngAfterViewInit(){
this.element.style.backgroundColor = 'red';
}
}
hover.directive.ts
import { AfterViewInit, Directive, ElementRef, Input } from '@angular/core';
import { After } from 'v8';
interface Options{
bgColor?: string; // ? 表示可选
}
// @Directive是个装饰器, 用来告诉Angular这是一个指令
@Directive({
selector: '[appHover]'
})
export class HoverDirective implements AfterViewInit {
@Input("appHover") appHover: Options = {}
// @Input 是一个装饰器,用来告诉Angular这个属性是一个输入属性
// Options是一个接口,用来定义输入属性的类型
element: HTMLElement
constructor(private elementRef: ElementRef) {
//elementRef是一个指向宿主元素的引用
//ElementRef是一个包装器,它包装了一个原生的DOM元素,可以通过nativeElement属性访问它
// console.log(elementRef);
this.element = elementRef.nativeElement;
}
ngAfterViewInit(){
this.element.style.backgroundColor = this.appHover.bgColor||'skyblue';
}
}
<!-- <div [appHover]="{bgColor: 'red' }">Hello Angular </div> -->
<div [appHover]="{}">Hello Angular </div>
hover.directive.ts
:
import { AfterViewInit, Directive, ElementRef, HostListener, Input } from '@angular/core';
import { After } from 'v8';
interface Options{
bgColor?: string; // ? 表示可选
}
// @Directive是个装饰器, 用来告诉Angular这是一个指令
@Directive({
selector: '[appHover]'
})
export class HoverDirective implements AfterViewInit {
@Input("appHover") appHover: Options = {}
// @Input 是一个装饰器,用来告诉Angular这个属性是一个输入属性
// Options是一个接口,用来定义输入属性的类型
element: HTMLElement
constructor(private elementRef: ElementRef) {
//elementRef是一个指向宿主元素的引用
//ElementRef是一个包装器,它包装了一个原生的DOM元素,可以通过nativeElement属性访问它
// console.log(elementRef);
this.element = elementRef.nativeElement;
}// 可以通过ElementRef指令拿到元素
ngAfterViewInit(){
this.element.style.backgroundColor = this.appHover.bgColor||'skyblue';
}
// 组件初始化完成后,才能获取到元素
@HostListener('mouseenter') enter(){
this.element.style.backgroundColor = "pink";
}
@HostListener('mouseleave') leave(){
this.element.style.backgroundColor = 'blue'
}
}
在 Angular 中,自定义指令是扩展和增强 DOM 元素行为的重要工具。你提到的
@Directive
装饰器和相关的概念(如@Input
、ElementRef
等)是创建和使用自定义指令的核心部分。以下是它们的含义及作用的详细解释。自定义指令分类
Angular 提供两种类型的指令:
- 结构型指令(Structural Directive) - 改变 DOM 的结构,例如
*ngIf
和*ngFor
。 - 用于动态添加、移除或替换元素。- 属性型指令(Attribute Directive) - 改变元素的外观或行为,例如
ngClass
和ngStyle
。 - 不改变 DOM 结构,仅修改属性或样式。创建自定义指令的关键工具
1.
@Directive
装饰器
@Directive
是定义自定义指令的装饰器。- 用法类似
@Component
,但它不包含模板或样式。示例:
import { Directive, ElementRef, Renderer2 } from '@angular/core'; @Directive({ selector: '[appHighlight]' // 使用时加 [] 表示属性型指令 }) export class HighlightDirective { constructor(private el: ElementRef, private renderer: Renderer2) { this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'yellow'); } }
- 作用:为目标元素添加黄色背景色。
- 使用:
2.
@Input
@Input
是一个装饰器,用于从父组件向指令传递值。- 通常结合 setter 使用,可以拦截并基于传入的值执行操作。
示例:
@Directive({ selector: '[appHighlight]' }) export class HighlightDirective { @Input() set appHighlight(color: string) { this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', color || 'yellow'); } constructor(private el: ElementRef, private renderer: Renderer2) {} }
- 作用:允许动态设置背景颜色。
- 使用:
3.
ElementRef
ElementRef
是 Angular 提供的类,用于直接访问 DOM 元素。- 属性
nativeElement
包含该 DOM 元素的引用。注意:直接使用
ElementRef
操作 DOM 存在安全风险(如 XSS 攻击)。通常建议搭配Renderer2
使用。示例:
4.
Renderer2
Renderer2
是一种安全操作 DOM 的抽象层。- 它提供的方法可以安全地修改 DOM,而不用直接访问
nativeElement
。示例:
constructor(private el: ElementRef, private renderer: Renderer2) { this.renderer.setStyle(this.el.nativeElement, 'color', 'blue'); // 安全地设置样式 }
5.
ViewContainerRef
和TemplateRef
这些主要用于结构型指令,帮助动态操作 DOM 结构。
TemplateRef
:表示一个嵌套模板,用于动态生成视图。ViewContainerRef
:表示视图容器,可以向其中添加或移除视图。示例:创建一个自定义的
*appIf
指令import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; @Directive({ selector: '[appIf]' }) export class AppIfDirective { @Input() set appIf(condition: boolean) { if (condition) { this.vcRef.createEmbeddedView(this.templateRef); // 添加视图 } else { this.vcRef.clear(); // 移除视图 } } constructor(private templateRef: TemplateRef<any>, private vcRef: ViewContainerRef) {} }
- 使用:
总结
工具 作用 @Directive
定义自定义指令,用于扩展 DOM 元素的行为。 @Input
从父组件传递数据到指令中,常用于动态控制指令的行为。 ElementRef
直接访问 DOM 元素。通常搭配 Renderer2
使用。Renderer2
提供安全操作 DOM 的方法,用于设置样式、属性等。 ViewContainerRef
表示视图容器,用于添加或移除动态视图,通常用于结构型指令。 TemplateRef
表示嵌套模板,配合 ViewContainerRef
使用,用于动态生成视图。