サンプルコードで身につける: 属性ディレクティブ — ngClass と ngStyle
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1ngClass にオブジェクトを渡す
ngClass の基本形で、「クラス名: 付ける条件」のオブジェクトを渡します。条件が true のクラスだけが要素に付くので、warn と error のように排他的な状態の見た目を1か所で切り替えられます。imports には CommonModule 一括ではなく、使うものだけを NgClass のように個別に登録するのが現在の推奨です。
import { Component } from '@angular/core';
import { NgClass } from '@angular/common';
@Component({
selector: 'app-alert',
// CommonModule 一括ではなく NgClass を個別に imports する
imports: [NgClass],
// 条件が true のクラスだけが付く
template:
'<p [ngClass]="{ warn: level === \'warn\', error: level === \'error\' }">' +
' ディスク使用率が高くなっています' +
'</p>',
styles: ['.warn { color: orange; } .error { color: red; font-weight: bold; }'],
})
export class AlertComponent {
level = 'warn'; // 'error' に変えると付くクラスも変わる
}
2ngClass に配列や文字列を渡す
ngClass はオブジェクトのほかに、クラス名の配列やスペース区切りの文字列も受け取れます。「常に付く基本クラス + 種類ごとの装飾クラス」のような組み合わせを、クラス側で1つの値にまとめて渡せるのが便利な点です。付け外しの条件が不要で、クラスの組み合わせだけを差し替えたいときはこの形が簡潔です。
import { Component } from '@angular/core';
import { NgClass } from '@angular/common';
@Component({
selector: 'app-chip',
imports: [NgClass],
// 配列でも 'chip chip-primary' という文字列でも同じ結果になる
template: '<span [ngClass]="chipClasses">おすすめ</span>',
styles: [
'.chip { padding: 4px 8px; border-radius: 9999px; }' +
' .chip-primary { background: teal; color: white; }',
],
})
export class ChipComponent {
// 基本クラス + 種類ごとのクラスをクラス側で組み立てる
chipClasses = ['chip', 'chip-primary'];
}
3ngStyle で値に応じたスタイルを当てる
幅や色のように「値そのものがデータから決まる」スタイルは、クラスの付け外しでは表現しきれないので ngStyle でまとめて渡します。進捗バーの幅をパーセントの値から作るのは、その代表的な実務例です。固定の見た目は styles のCSSに置き、値で変わる部分だけを ngStyle に絞るのが読みやすい分担です。
import { Component, signal } from '@angular/core';
import { NgStyle } from '@angular/common';
@Component({
selector: 'app-progress-bar',
imports: [NgStyle],
// 値によって変わるスタイルだけを ngStyle で渡す
template:
'<div class="bar"' +
' [ngStyle]="{ width: percent() + \'%\', background: barColor() }">' +
'</div>',
// 固定の見た目は通常のCSSに置く
styles: ['.bar { height: 8px; border-radius: 4px; }'],
})
export class ProgressBarComponent {
percent = signal(72);
barColor = signal('teal');
}
4[class.xxx] と ngClass の使い分け
切り替えるクラスが1つだけなら、以前学んだ [class.xxx] の単発バインドが最も簡潔で、imports の追加も不要です。複数のクラスを同時に切り替えるなら ngClass にまとめる、という使い分けを1つのテンプレートで対比した例です。「1つなら class バインド、複数なら ngClass」という目安を持っておくと、実務でも迷わず書けます。
import { Component, signal } from '@angular/core';
import { NgClass } from '@angular/common';
@Component({
selector: 'app-menu-item',
imports: [NgClass],
template:
// クラスが1つだけなら [class.xxx] が簡潔(imports も不要)
'<li [class.selected]="isSelected()">通知設定</li>' +
// 複数のクラスを同時に切り替えるなら ngClass にまとめる
'<li [ngClass]="{ selected: isSelected(), disabled: isDisabled() }">' +
' プラン変更' +
'</li>',
styles: ['.selected { font-weight: bold; } .disabled { opacity: 0.5; }'],
})
export class MenuItemComponent {
isSelected = signal(true);
isDisabled = signal(false);
}
5状態から computed でクラスを組み立てる実務例
注文ステータスのような状態に応じてバッジの見た目を変える、実務でよくある形です。どのクラスを付けるかの判定を computed に切り出し、テンプレートには [ngClass]="badgeClass()" と結果だけを渡します。条件がテンプレートに散らばらないので、ステータスが増えたときも computed の1か所を直すだけで済みます。
import { Component, computed, signal } from '@angular/core';
import { NgClass } from '@angular/common';
type Status = 'pending' | 'shipped' | 'canceled';
@Component({
selector: 'app-order-status',
imports: [NgClass],
// 判定は computed に寄せ、テンプレートには結果だけを渡す
template: '<span class="badge" [ngClass]="badgeClass()">{{ status() }}</span>',
styles: [
'.badge { padding: 2px 8px; } .pending { background: gold; }' +
' .shipped { background: teal; color: white; }' +
' .canceled { background: gray; color: white; }',
],
})
export class OrderStatusComponent {
status = signal<Status>('shipped');
badgeClass = computed(() => ({
pending: this.status() === 'pending',
shipped: this.status() === 'shipped',
canceled: this.status() === 'canceled',
}));
}