自作ディレクティブをまとめるライブラリープロジェクトを作成
Angularでは、ワークスペース内に複数のプロジェクトを定義できます(過去記事、次世代Webアプリケーションフレームワーク「Angular」の活用 第14回も参照)。本記事では、自作ディレクティブの再利用を想定して、Webページ本体のプロジェクトとは別のライブラリープロジェクトに自作ディレクティブを実装します。
リスト2のコマンドで、ワークスペースとライブラリープロジェクトを作成できます。
ng new p002-attr #...(1) cd p002-attr #...(2) ng generate library my-libs --prefix=my #...(3) ng generate directive marker1 --project my-libs #...(4)
(1)でワークスペース(とWebページのプロジェクト)を作成後、(2)でそのフォルダーに移動して、(3)で「my-libs」という名前のライブラリープロジェクトを追加します。このとき、「--prefix」オプションで、ライブラリープロジェクトの接頭辞myを指定します。ディレクティブのスケルトンコードを作成するときは、(4)の通り「--project」オプションで生成先のプロジェクトを指定します。生成されたディレクティブには、ライブラリープロジェクトの接頭辞myがディレクティブ名に付与されます(詳細は後述)。
実装したディレクティブを他のプロジェクトから利用できるように、ライブラリープロジェクトのモジュール定義ファイル(my-libs.module.ts)で、ディレクティブのクラスをエクスポートします。
@NgModule({
(略)
exports: [
Marker1Directive // ディレクティブをエクスポート
]
})
export class MyLibsModule { }
ディレクティブを実装した後、リスト4のコマンドでライブラリープロジェクトをビルドします。ビルドされたライブラリープロジェクトが、dist/my-libsフォルダーに出力されます。
ng build --project=my-libs
Webページプロジェクトのモジュール定義ファイル(app.module.ts)では、リスト5の通りライブラリープロジェクトのモジュールをインポートして、ライブラリープロジェクトに含まれるディレクティブを利用できるようにします。
@NgModule({
(略)
imports: [
BrowserModule,
MyLibsModule // ライブラリープロジェクトのモジュールをインポート
],
(略)
})
export class AppModule { }
自作の属性ディレクティブを実装
属性ディレクティブの実装方法を、マーカーペンのように要素を強調表示するディレクティブのサンプル(p002-attr)を用いて説明します。
属性ディレクティブの基本
図1は、背景色とタイトル(マウスオーバー時に表示される文言)を設定する属性ディレクティブです。
図1のディレクティブは、リスト6の通り実装します。
@Directive({ // ...(1)
selector: '[myMarker1]' // ...(2)
})
export class Marker1Directive {
// コンストラクター ...(3)
constructor(private el: ElementRef) {
this.el.nativeElement.style.backgroundColor = 'yellow'; // ...(4)
this.el.nativeElement.title = 'ここは大事なのでマーク'; // ...(5)
}
}
ディレクティブのクラスには@Directiveデコレーター(1)を付与して、デコレーターのselector変数にディレクティブの名前(2)を指定します。リスト2(4)のコマンドでディレクティブのクラスを生成すると、リスト2(3)でライブラリープロジェクトに設定した接頭辞myがディレクティブ名に付与されて「myMarker1」となります。重複を防ぐため、ディレクティブ名には接頭辞を付与することが推奨されています。
コンストラクター(3)で、要素に対応するElementRef型変数elを、依存性注入で受け取ります。受け取ったelのnativeElementプロパティでHTML要素を取得して、属性を指定します。ここでは(4)で背景色、(5)でタイトルを指定します。
ディレクティブを利用する側は、リスト7の通り、HTMLタグの属性と似た記法でディレクティブ名「myMarker1」を記述します。ディレクティブを記述した<span>タグに囲まれた部分に、背景色とタイトルが設定されます。
<span myMarker1>鎌倉幕府</span>
属性ディレクティブを画面操作に反応させる
属性ディレクティブには、ユーザーの画面操作に反応する処理を実装できます。マウスオーバー時に背景色を変える図2のサンプルで説明します。
画面操作に反応する処理は、@HostListenerデコレーターの引数にイベントの種類を指定したメソッドで実装します。リスト8はmouseenter(マウスカーソルが当たったときのイベント)の処理で、背景色を変更しています。サンプルコードでは、mouseleave(マウスカーソルが離れたときのイベント)の処理も同様に実装しています。
@HostListener('mouseenter') onMouseEnter() {
this.el.nativeElement.style.backgroundColor = 'pink';
}
属性ディレクティブにパラメーターを指定
属性ディレクティブにパラメーターを指定する例を、図3のサンプルで説明します。このサンプルでは、背景色とタイトルをパラメーターとして指定できます。
このサンプルでは、リスト9の通り、背景色をmyMarkerColor、タイトルをmyMarkerTitleで指定します。(1)のように値を直接指定するほか、(2)のようにコンポーネント変数の値をデータバインディングで指定することもできます。
<!-- 値を直接指定 ...(1) --> <span myMarker3 myMarkerColor="#ffaa00" myMarkerTitle="絶対覚える!!">江戸幕府</span> <!-- データバインディングでコンポーネント変数の値を指定 ...(2)--> <span myMarker3 [myMarkerColor]="markerColor" [myMarkerTitle]="markerTitle">江戸幕府</span>
リスト9で指定されたパラメーターを受け取るには、ディレクティブのクラスに、@Inputデコレーターを付与したプロパティを実装します。
@Input('myMarkerColor') set myMarkerColor(markerColor: string) {
this.el.nativeElement.style.backgroundColor = markerColor;
}
@Input('myMarkerTitle') set myMarkerTitle(markerTitle: string) {
this.el.nativeElement.title = markerTitle;
}
値が変更されるたびに要素に反映させるため、setキーワードを利用して、設定値を引数で受け取るメソッド(いわゆるセッター)としてプロパティを実装します。ここでは、メソッドの引数で受け取った背景色やタイトルを、要素に反映するよう実装しています。
[補足]ディレクティブに直接パラメーターを指定できるようにする方法
リスト11の[myMarker3a]のように、ディレクティブに直接パラメーターを指定させることもできます。
<span [myMarker3a]="markerColor" [myMarkerTitle]="markerTitle">江戸幕府</span>
ディレクティブに指定されたパラメーターを受け取るには、@Inputデコレーターの引数にディレクティブ名(ここではmyMarker3a)を設定します。
@Input('myMarker3a') set myMarkerColor(markerColor: string) {
