Angularでテーブルの選択行に色付け
AngularでDOM操作で直接値を編集したり景観(CSS)を変更したりするのはよくないという話を聞きます。
バインディングしているデータを変更することにより、それらのほとんどが実現できるからというのがその理由なのだそうですが、クリックしたテーブルの行に色をつけるにはどうするのか? と躓いたことがあったので、ここにメモを残しておきます。
テーブルの選択行に色を付ける
Angularでの装飾は基本的にCSSを利用します。また、クラスを動的に設定する方法にクラスバインディングという方法があります。
クラスバインディングを使って、スクリプト側に保持する選択行と行のindexが同じ場合はselectedクラスが設定されるようにします。そしてCSSでselectedクラスにbackground-colorを設定します。
実際のコードは次のようになりました。
component.ts
...
export class SomeComponent implements OnInit {
//プロパティに選択中の行のインデックスを保持します。
public selectedRow: number = -1;
//テーブルの行と列を保持するプロパティです。
public rows: number[][]=[];
....
public clickRow(rowIndex) {
//クリックイベントでプロパティを変更します
this.selectedRow = rowIndex;
}
}
...
component.html
...
<table>
<tr *ngFor="let row of rows; let ind = index" [class]="ind === selectedRow ? 'selected' : ''" (click)="selectRow(ind)">
<td *ngFor="let col of row">{{col}}</td>
</tr>
</table>
...
component.css
...
tr.selected {
background-color: #ffcccc;
}
...
データバインディング
Angularのデータバインディングには、その方向により3種に分類されます。
ひとつは今回のクラスバインディングで利用したデータソース(スクリプト)からビューターゲット(HTML)にデータを流すもので[ターゲット]="式"の形で表します。
クラスの他には、補間、プロパティ、属性、スタイルに適用できます。この補完バインディングだけ他とは記法が違います。HTML中に{{ 何か }}として、表示を補うものです。
もう一つはビューターゲットから、スクリプトへのデータを流すものです。これはイベントで使われます。(click)が一番なじみがあるものでしょう。
最後は双方向です。これは[(ngModel)]でおなじみです。
ちなみに先ほどのクラスバインディングはクラス名を直接渡していましたが、事前に指定してその付与と除去を行うとう方法でも実現できます。
その場合は、[class.selected]="ind === selectedRow"という風にします。
もし複数のクラスをバインディングしたいならオブジェクトを渡します。スクリプトのbln1、bln2のboolean値の変化により、Trueなら対となったクラスが付与されFalseなら除去されます。
[class]="{'class1' : bln1, 'class2' : bln2}"
ディレクティブとバインディング
ディレクティブでも同じことができます。
ディレクティブとは和訳すると「指令」で、Angularのコマンド群を指します。これは3種に大別されます。ひとつはコンポーネントを表示するために<コンポーネント名>とするディレクティブ。ふたつ目はngIfを代表とするページを構築する際に利用する「構造ディレクティブ」、最後に見た目やDOM操作をする「属性ディレクティブ」です。
属性ディレクティブにはNgClass(クラスの付与と除去)、NgStyle(CSSの付与と除去)、NgModel(双方向データバインディング)があります。
クラスの動的付与はこの中のNgClassディレクティブで実現できます。
属性ディレクティブとバインディングの違いが分かりづらいですが、[ngClass]="bln ? 'a' : 'b'"の場合は、ディレクティブで受け取った値をクラスにセットする指示なのに対し、 [class]="bln ? 'a' : 'b'"としたときは、バインディングでclassプロパティ値にデータを直接あてはめているイメージです。
正確に理解したい場合はHTMLの属性とDOMプロパティの違いを理解する必要があると思います。
その際は、MDN:「content 属性 と IDL属性」やJAVASCRIPT.INFO「属性とプロパティ」を読み解いていただければと思いますが、重要なポイントはHTML属性とJavaScriptプロパティ別物では常に1対1で結びついて同期が行われているものではないということです。
先に紹介した公式ページに属性バインディングのサンプルとして書かれている「[attr.aria-label]="help"」が「例外」だというのはそこからくるものだと思います。
ちなみに先ほどクラスバインティングで使った記法はNgClassディレクティブでも利用できます。