🎉

【Angular】コンポーネントはわかるけど、ディレクティブがわからない人のための記事

2020/10/15に公開

まず結論から

まずは、ディレクティブは、「コンポーネントから見た目(HTML&CSS)をなくしたもの」と思ってもらうといいです。(厳密には違いますが、正しいことはこの記事の最後に書きます。)

ソースコードを見てみよう

早速ですが、ディレクティブのソースコード例です。とりあえず細かい中身は無視してください。

(コードはStackblitzで公開しています https://stackblitz.com/edit/directive-sample )

なんとなくコンポーネントと似ているけど、@Componentじゃなくて@Directiveであること、HTMLやCSSの宣言を行っていないことがわかります。

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

@Directive({
  selector: '[appChangeColorOnClick]'
})
export class ChangeColorOnClickDirective {

  constructor(private el: ElementRef) { }

  @Input() defaultColor: string;

  @Input('appChangeColorOnClick') color: string;

  @HostListener('click') onClick() {
    this.el.nativeElement.style.backgroundColor = this.color || this.defaultColor;
  }
}

使い方

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

selectorで宣言した名前[appChangeColorOnClick]で、他のコンポーネントから呼び出すことができます。(こういった部分もコンポーネントと似てますよね。)

呼び出す例がこちら。

<h1>Directive sample</h1>

<div class="test" [appChangeColorOnClick]="color">ここをクリックすると背景色が変わります</div>

<div class="test" [appChangeColorOnClick]="color" defaultColor="red">
  ここをクリックすると背景色が変わります
</div>

divタグに指定しているのがおわかりいただけるでしょうか?こんなふうにHTMLタグやコンポーネントなど任意の要素に指定するという使い方をします。

要素に対し、引っ付き虫のようにディレクティブが引っ付くイメージをもっていただけると良いです。

実はこれがディレクティブのメリットであり、「1つディレクティブを作ってしまえば、いろんなものに引っ付けて使い回せる」のが便利なのです。

詳細にディレクティブのコードを見てみる

まずは言葉の定義ですが、先程の引っ付き虫を思い出してください。引っ付いた先のことを「ホスト要素」と呼びます。(今回だとdivタグ)

ElementRef

コンストラクタのElementRef型の引数に、ホスト要素への参照が注入されています。ElementRefのnativeElementプロパティにより、直接ホスト要素のDOMへのアクセスが可能です。

@Input

これはコンポーネントの@Inputと同じものだと考えてもらえばほぼ大丈夫ですが、よくよく見るとselectorと同じ名前のInputがありますね。これは同じ名前でディレクティブを呼び出しつつ値も渡すということを同時にできるようにするものです。

@HostListener

ホスト要素で起こったイベントを監視するのでHostListenerです。例えば'click'だと、ホスト要素をクリックされたときに実行する処理を書きます。今回の場合は、クリックされたら@Inputで渡された値に背景色を変えます。

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

@Directive({
  selector: '[appChangeColorOnClick]'
})
export class ChangeColorOnClickDirective {

  constructor(private el: ElementRef) { }

  @Input() defaultColor: string;

  @Input('appChangeColorOnClick') color: string;

  @HostListener('click') onClick() {
    this.el.nativeElement.style.backgroundColor = this.color || this.defaultColor;
  }
}

より正確な話

ディレクティブ=「コンポーネントから見た目(HTML&CSS)をなくしたもの」という説明を最初にしましたが、これは実は逆で、コンポーネント=「ディレクティブに見た目をつけたもの」です。

Angular公式ドキュメントによると、ディレクティブは以下の3種類に分類されます。

  • コンポーネント
  • 構造ディレクティブ
  • 属性ディレクティブ

今回紹介したディレクティブは「属性ディレクティブ」です。今回はオリジナルのディレクティブを作りましたが、有名なものだとFormsModuleに含まれる[ngModel][formGroup]なども属性ディレクティブです。

構造ディレクティブとは、例えば有名なものだと*ngIf*ngForなどといった、DOMの構造自体を変更できるディレクティブです。構造ディレクティブは先頭にアスタリスク*がつきます。こちらも自分オリジナルのものを作ることができます。(公式ドキュメントで、*ngIfの逆を行う*appUnlessを作る というものがあります)

参考

Angular 日本語ドキュメンテーション - 属性ディレクティブ

Angular 日本語ドキュメンテーション - 構造ディレクティブ

Discussion