Angular 4:自訂Directive


我們定義了一個component,如果我們希望透過程式改變component內的背景顏色,過程不免是在component上加入一些binding的方式來動態改變component的style或class。




如果又有下一個component也要改變背景顏色,那麼就得對另一個component再改一次程式,如果要調整的數量一多,會變得維護不方便,而Angular提供了使用者自定義directive的方法,可以加入在html element上或是component上,提供對element調整的效果。

要建立自己客製的Directive,可以透過angluar-cli:

ng g d DirectiveName


來建立Directive,

或是直接建立檔案


import { Directive} from '@angular/core';

@Directive({
  selector: '[appMyCustom]'
})

export class MyCustomDirective {
  constructor() {}
}

如果是手動建立檔案,要記得在app.module內的declarations內加入MyCustomDirective設定。

如果html上有一段簡單的html,我們加入自己定義的directive:

<p appMyCustom >Test Directive</p>

接著要寫一個可以調整<p> element的directive,有幾種方式可以使用,下列一一列出:

1. 透過ElementRef調整Element

在自己建立好之Directive上,於constructor可以傳入ElementRef物件:

import { Directive, ElementRef, OnInit } from '@angular/core';

@Directive({
  selector: '[appMyCustom]'
})
export class MyCustomDirective implements OnInit {

  ngOnInit() {
    this.elementRef.nativeElement.style.backgroundColor = 'green';
  }

  constructor(private elementRef: ElementRef) {
  }
}


則constructor傳入的就是<p> element的reference,我們之後可以透過ElementRef直接對element做修改與調整,如上面程式就是直接修改了<p>的背景顏色,雖然最方便也最簡單,但一般比較不建議用這方式。


2. 使用Renderer


import { Directive, ElementRef, OnInit } from '@angular/core';

@Directive({
  selector: '[appMyCustom]'
})
export class MyCustomDirective implements OnInit {

  ngOnInit() {
    this.renderer.setStyle(this.elementRef.nativeElement, 'background-color', 'blue');
  }

  constructor(private elementRef: ElementRef, renderer: Renderer2) {}
}


第二種方式是透過Renderer2來調整element,於constructor內傳入,並且呼叫其提供的方式修改element,如程式上的setStyle,使用方式:

setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2) : void

即是把elementRef傳入,指定css屬性與值即可,使用起來更方便。


Renderer2相關使用方式可以參考官網:Angular:Renderer2


3. 使用HostBinding與HostListener

HostBinding與HostListener可以說就是在自己所建的directive內綁定了element的某個屬性/事件,這兩個類別可以從angular/core內匯入,直接從程式碼來看會比較ㄑㄧ:

HostBinding是用來綁定element的屬性到一個變數上,下面程式碼就是將
的 style.backgroundColor綁定在backgroundColor變數上,讓這個style的值可以透過Angular的變數直接連動。
  @HostBinding('style.backgroundColor') backgroundColor: string;

HostListener是用來監聽發生在element上的特定事件,像是滑鼠移入移出(mouseenter/ mouseleave),像是這類javascript的事件,可以透過HostListener來監聽,在element觸發特定事件時,同時執行Angular內的程式,用程式來看:
  
@HostListener('mouseenter') mouseover(event: Event) {
    this.renderer.setStyle(this.elementRef.nativeElement, 'background-color', 'blue');
}

當滑鼠指標移入<p>的範圍內,就會執行mouseover的function,將<p>的背景顏色設為藍色,同樣的也可以再增加一個mouseleave的HostListener,在滑鼠移開後將<p>的背景顏色設回原本的顏色:
  

@HostListener('mouseleave') mouseleave(event: Event) {
    this.renderer.setStyle(this.elementRef.nativeElement, 'background-color', 'transparent');
}


4. 讓Directive允許傳入設定參數

Angular的Directive允許使用者可以傳入一些參數作為Directive內使用,以剛剛的例子來看,在滑鼠移入時會變為藍色,不過使用的人可能會希望滑鼠移入時變為其他顏色,遇到這情況,我們可以透過類似component的attribute binding方式來做到,同樣透過Input的方式,允許定義的directive可以綁定某個屬性,並且可以傳入參數,來達到想要的調整效果:

首先在自訂的directive內加入Input
  
  @Input() hilightColor: string = 'blue';  //預設為藍色

接著在<p> element的地方就可以綁定hilightColor屬性:

<p appMyCustom [hilightColor]="'yellow'">Test Directive</p>
(要注意雙引號內一定要有單引號!)

這時候會把hilightColor設定為黃色,因此如果希望在mouseenter事件觸發時,改變<p>的背景為黃色,就可以稍微調整HostListener程式為以下即可:
  
@HostListener('mouseenter') mouseover(event: Event) {
     this.backgroundColor = this.hilightColor;}


同理也可以用同樣的方式設定mouseleave時<p>背景顏色的變化。


直得一提的是,這一段程式碼可以稍微再簡潔一些的寫法,可以從

<p appMyCustom [hilightColor]="'yellow'">Test Directive</p>


調整為

  
<p appMyCustom hilightColor="yellow">Test Directive</p>


一樣可以正常運作,方式一樣是attribute binding,使用上比較方便,不過還是要知道這是透過attribute binding的方式做到的。

另外,angular如何判斷在<p>內的hulight是屬於我們自訂的directive的還是<p>的呢?
如今我們自己定義了一個attribute - src 在directive內,結果我們用在以下情況:


<img  appMyCustom src="Url">
並且在directive內加入
@Input() src: string = '';


img內有src屬性,同時我們的directive內也有src屬性,誰會吃到Url屬性呢?


實際測試出來的結果是,img與appMyCustom都會吃到src這個屬性的值,所以在這情況,img可以抓到圖片,同時appMyCustom也可以抓到src傳入的參數Url。
但基本上來說,Angular會先找自己定義的directive看有沒有這個屬性,接著再找native element,如果兩者都有,兩者都可以吃到傳入屬性的內容。


自訂directive大致上這樣,還有一個比較特別的structural directive,就記錄在下一篇了。



沒有留言:

張貼留言

Java Spring Framework 筆記 - Autowiring (2)

這篇記錄透過 Annotation來做到 Spring的 autowiring。