[Angular] Day15. Attribute directives

在前几天中有介绍了 Angular 中内建的一些 attribute directive,但是在实际开发专案时可能会遇到内建 attribute directive 无法处理的问题,这时候就需要建立客制化 attribute directive,本章就会介绍如何建立属於自己的 attribute directive。

https://ithelp.ithome.com.tw/upload/images/20210821/201247672vA3mJw784.jpg


Building an attribute directive

来跟着 Angular 的官方文档做一个练习,将会建立一个客制化的 attribute directive,将一个宿主 element 的背景颜色设置为黄色的 directive。

  1. 首先先使用 Angular CLI 建立一个 directive

    ng generate directive highlight
    ng g d highlight
    

    使用这个 CLI 指令可以自动建立一个名为 highlight.directive.ts 和 highlight.directive.spec.ts 的档案,并且会在被加入到 app.module.ts 的 declares ,所以如果不是用 CLI 建立 directive file 时,要记得手动将它加入到 declares 里面喔!

  2. 在 highlight.directive.ts 中从 @angular/core 中导入 ElementRef

    import { Directive, ElementRef } from '@angular/core';
    
    @Directive({
      selector: '[appHighlight]'
    })
    export class HighlightDirective {
        constructor(el: ElementRef) {                           // (1)
           el.nativeElement.style.backgroundColor = 'yellow';   // (2)
        }
    }
    
    • (1): 在 constructor 中将 ElementRef inject 到 HighlightDirective 中
    • (2): 透过使用 ElementRef 的 nativeElement property 授与对宿主 DOM element 的直接访问权限,并将他的 style 改变为黄色
  3. 在 app.component.html 中添加一个 element 并将刚刚客制化的 directive 加在上面

    <!-- app.component.html -->
    
    <p appHighlight>Highlight me!</p>
    

https://ithelp.ithome.com.tw/upload/images/20210809/20124767f71HrHwdCO.png

Angular 会将 HighlightDirective 给实例化并将对 <p> element 的 reference injects 到 highlight.directive.ts 的 constructor 中,让他可以对这个元素的 style 进行更改。


Handling user events

除了改变 element 之外也可以在客制化的 directive 中检测用户在画面中的事件(鼠标移入或移出)并对事件进行不同的响应。

举个例子,保持上面的例子只是将画面上的 <p> element 绑定一个 hover 事件,当鼠标移到上面时将他背景颜色改为黄色

  1. @angular/core中导入 HostListener

    import { Directive, ElementRef, HostListener } from '@angular/core';
    
  2. 更改 highlight.directive.ts 中的 method 用於响应事件

    import { Directive, ElementRef, HostListener } from '@angular/core';
    
    @Directive({
      selector: '[appHighlight]'
    })
    export class HighlightDirective {
      constructor(private el: ElementRef) {}
    
      @HostListener('mouseenter') onMouseEnter() {               // (1)
        this.highlight('yellow');
      }
    
      @HostListener('mouseleave') onMouseLeave() {               // (2)
        this.highlight('');
      }
    
      private highlight(color: string) {                         // (3)
        this.el.nativeElement.style.backgroundColor = color;
      }
    }
    
    • (1): 当鼠标移动到元素上时触发此 method 对行为作出响应
    • (2): 当鼠标从元素上离开时触发此 method 对行为作出响应
    • (3): 对绑定元素的 style 进行更改

img

在画面中可以看,当我们的鼠标移动到 <p> element 上面时会触发 highlight.directive.ts 中的 method 将他的背景颜色改为黄色。


Passing values into an attribute directive

除了将你要更改的内容写死在 directive 之外(上面例子是写死 hover 时背景变黄色),你也可以透过传递参数的方式,动态的传递你所期望改变的内容,一样拿上面的例子来延伸吧

  1. 首先先改变 highlight.directive.ts 的内容

    import { Directive, ElementRef, HostListener, Input } from '@angular/core';  // (1)
    
    @Directive({
      selector: '[appHighlight]'
    })
    export class HighlightDirective {
      @Input() appHighlight = '';                              // (2)
      constructor(private el: ElementRef) {}
    
      @HostListener('mouseenter') onMouseEnter() {
        this.highlight(this.appHighlight);                     // (3)
      }
    
      @HostListener('mouseleave') onMouseLeave() {
        this.highlight('');
      }
    
      private highlight(color: string) {
        this.el.nativeElement.style.backgroundColor = color;
      }
    }
    
    • (1): 从 @angular/core 中引入 Input
    • (2): 新增一个 property 并使用 @Input() 将他装饰为从父层传递的数据
    • (3): 当鼠标移动到 element 时更改的颜色从写死的状态变更为绑定 @Input() 的内容
  2. 更改 app.component.html 的内容,将颜色传递给 HighlightDirective

    <!-- app.component.html -->
    
    <p [appHighlight]="'red'">Highlight me!</p>
    

img

可以看到画面中,当鼠标移动到 element 时从黄色变为我们传递给 directive 的红色了。


Binding to a second property

除了可以传递参数给 directive 之外还可以对他设定一个预设值,以上面例子来说,我们可以设定一个预设值,直到使用者改变颜色後才变为指定的颜色

  1. 更改 highlight.directive.ts 中的内容

    import { Directive, ElementRef, HostListener, Input } from '@angular/core';
    
    @Directive({
      selector: '[appHighlight]'
    })
    export class HighlightDirective {
      @Input() appHighlight = '';
      @Input() defaultColor = '';                              // (1)
      constructor(private el: ElementRef) {}
    
      @HostListener('mouseenter') onMouseEnter() {
        this.highlight(this.appHighlight || this.defaultColor || 'red');
      }
    
      @HostListener('mouseleave') onMouseLeave() {
        this.highlight('');
      }
    
      private highlight(color: string) {
        this.el.nativeElement.style.backgroundColor = color;
      }
    }
    
  2. 在 app.component.ts 中添加一个 property 与 method

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      color = '';                            // (1) 
    
      onClick (color: string) {              // (2)
        this.color = color;
      }
    }
    
    • (1): 新增一个 property 用於传给 directive
    • (2): 新增一个 method 当画面中的按钮被点击时触发
  3. 在 app.component.html 中新增三个 <button> 并将 directive 加上预设值

    <!-- app.component.html -->
    
    <button (click)="onClick('green')">Green</button>
    <button (click)="onClick('yellow')">Yellow</button>
    <button (click)="onClick('cyan')">Cyan</button>
    
    <p [appHighlight]="color" defaultColor="violet">Highlight me too!</p>
    

img

在画面中可以看到,当我们没有点击画面中的 <button> 时我们的鼠标移动到 element 时显示的颜色是预设的颜色,而当点击了其中一个按钮後就换更新为指定的颜色。


结论

本章介绍了如何建立与使用客制化的 directive,可以透过使用 ElementRef 的 nativeElement property 授与对宿主 DOM element 的直接访问权限,可以使用 HostListener 用来处理画面中使用者的行为,也可以使用之前提到的 @Input() 用於传递参数进到 directive 中,这个客制化的 directive 对於要处理特别的情况时非常好用。

下一章将介绍如何创建客制化的 structural directive并介绍 directive 使如何工作的、 Angular 如解释速记以及如何添加 template 的保护 property 用於捕获 template 的错误,那我们就明天见罗!


Reference


<<:  Day1 前言

>>:  @Day15 | C# WixToolset + WPF 帅到不行的安装包 [安装包上的图片]

Day25:NavigationView

前言 前面两天刻了两个 view, 现在要用 Navigation 来把它们连接起来。 实作 在 R...

职位描述 (job description)

职位描述是职位设计的输出之一,它考虑了“分工”的原则,需要人力资源部和研发部进行协作。职位描述是确定...

[2021铁人赛 Day23] Cryptography 密码学题目 01

引言 我们前几天已经把 General Skills 完成了,所以今天开始 (已经没剩几天了 就至...

成为工具人应有的工具包-21 RegScanner

RegScanner 今天来认识看名字应该是注册表扫描?的小工具.... RegScanner 是可...