Skip to content

5.2 Custom Directives

5.2 Custom Directives

需求: 为元素设置默认背景颜色, 鼠标移入时的背景颜色以及移出时的背景颜色.

<div [appHover]="{ bgColor: 'skyblue' }">Hello Angular</div>

ng g d directives/hover

Screenshot 2025-01-17 at 15.21.51

Screenshot 2025-01-17 at 15.25.44

Screenshot 2025-01-17 at 15.25.56

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);
  }

}

Screenshot 2025-01-17 at 15.31.41

Screenshot 2025-01-17 at 15.32.04

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);
  }

}

Screenshot 2025-01-17 at 15.47.19

Screenshot 2025-01-17 at 15.47.40

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';
  }

}

Screenshot 2025-01-17 at 15.48.53

Screenshot 2025-01-17 at 15.48.58

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';
  }

}

Screenshot 2025-01-17 at 15.55.47

Screenshot 2025-01-17 at 15.56.21

<!-- <div [appHover]="{bgColor: 'red' }">Hello Angular </div> -->
<div [appHover]="{}">Hello Angular </div>

Screenshot 2025-01-17 at 15.57.49

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'
  }

}

Screenshot 2025-01-17 at 17.05.07

Screenshot 2025-01-17 at 17.05.21

在 Angular 中,自定义指令是扩展和增强 DOM 元素行为的重要工具。你提到的 @Directive 装饰器和相关的概念(如 @InputElementRef 等)是创建和使用自定义指令的核心部分。以下是它们的含义及作用的详细解释。

自定义指令分类

Angular 提供两种类型的指令:

  1. 结构型指令(Structural Directive) - 改变 DOM 的结构,例如 *ngIf*ngFor。 - 用于动态添加、移除或替换元素。
  2. 属性型指令(Attribute Directive) - 改变元素的外观或行为,例如 ngClassngStyle。 - 不改变 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');
  }
}
  • 作用:为目标元素添加黄色背景色。
  • 使用
<p appHighlight>这段文字会被高亮。</p>

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) {}
}
  • 作用:允许动态设置背景颜色。
  • 使用
<p [appHighlight]="'blue'">这段文字会被设置为蓝色背景。</p>
<p [appHighlight]="'green'">这段文字会被设置为绿色背景。</p>

3. ElementRef

  • ElementRef 是 Angular 提供的类,用于直接访问 DOM 元素。
  • 属性 nativeElement 包含该 DOM 元素的引用。

注意:直接使用 ElementRef 操作 DOM 存在安全风险(如 XSS 攻击)。通常建议搭配 Renderer2 使用。

示例:

constructor(private el: ElementRef) {
  this.el.nativeElement.style.color = 'red'; // 直接操作元素
}

4. Renderer2

  • Renderer2 是一种安全操作 DOM 的抽象层。
  • 它提供的方法可以安全地修改 DOM,而不用直接访问 nativeElement

示例:

constructor(private el: ElementRef, private renderer: Renderer2) {
  this.renderer.setStyle(this.el.nativeElement, 'color', 'blue'); // 安全地设置样式
}

5. ViewContainerRefTemplateRef

这些主要用于结构型指令,帮助动态操作 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) {}
}
  • 使用
<p *appIf="true">这段文字会显示。</p>
<p *appIf="false">这段文字不会显示。</p>

总结

工具 作用
@Directive 定义自定义指令,用于扩展 DOM 元素的行为。
@Input 从父组件传递数据到指令中,常用于动态控制指令的行为。
ElementRef 直接访问 DOM 元素。通常搭配 Renderer2 使用。
Renderer2 提供安全操作 DOM 的方法,用于设置样式、属性等。
ViewContainerRef 表示视图容器,用于添加或移除动态视图,通常用于结构型指令。
TemplateRef 表示嵌套模板,配合 ViewContainerRef 使用,用于动态生成视图。