@HostBinding
| 対応: | Angular 14(2022) |
|---|
『Angular』の @HostBinding は、ディレクティブやコンポーネントのホスト要素(そのクラスが適用されているDOM要素)のプロパティやCSSクラス・スタイルに、クラスのプロパティを直接バインドするためのデコレータです。テンプレートに [プロパティ名] を書かずに、クラス側の宣言だけでホスト要素の外観や属性を制御できます。
基本的な使い方
| 書き方 | 説明 |
|---|---|
@HostBinding('属性名') | ホスト要素の HTML 属性をクラスのプロパティにバインドします。 |
@HostBinding('class.クラス名') | boolean 値に応じてホスト要素へ CSS クラスを付与・除去します。 |
@HostBinding('style.プロパティ名') | ホスト要素のインラインスタイルをクラスのプロパティにバインドします。 |
@HostBinding('attr.属性名') | ホスト要素の HTML 属性(aria-* など)をクラスのプロパティにバインドします。 |
CSS クラスをバインドする
// active-item.directive.ts
// @HostBinding('class.active') を使って、ホスト要素に active クラスを付与・除去します
// boolean 値が true のときにクラスが付き、false のときに外れます
import { Directive, HostBinding, HostListener } from '@angular/core';
@Directive({
standalone: true,
// テンプレートで [appActiveItem] 属性を付けた要素にこのディレクティブを適用します
selector: '[appActiveItem]',
})
export class ActiveItemDirective {
// true のときにホスト要素へ active クラスを付与します
@HostBinding('class.active') isActive: boolean = false;
// ホスト要素がクリックされるたびに選択状態を切り替えます
@HostListener('click')
onClick(): void {
this.isActive = !this.isActive;
}
}
// app.component.ts
// リスト項目に [appActiveItem] を付けると、クリックで active クラスが付きます
import { Component } from '@angular/core';
import { ActiveItemDirective } from './active-item.directive';
@Component({
standalone: true,
imports: [ActiveItemDirective],
selector: 'app-root',
template: `
<ul style="list-style:none; padding:0;">
<li [appActiveItem] style="padding:8px; cursor:pointer;">項目A</li>
<li [appActiveItem] style="padding:8px; cursor:pointer;">項目B</li>
<li [appActiveItem] style="padding:8px; cursor:pointer;">項目C</li>
</ul>
`,
styles: [`
.active {
background-color: #d0e8ff;
font-weight: bold;
}
`],
})
export class AppComponent {}
インラインスタイルをバインドする
// highlight.directive.ts
// @HostBinding('style.backgroundColor') でホスト要素の背景色を動的に変えます
// マウスが乗ったときと離れたときでスタイルを切り替えています
import { Directive, HostBinding, HostListener, Input } from '@angular/core';
@Directive({
standalone: true,
selector: '[appHighlight]',
})
export class HighlightDirective {
// テンプレート側で [appHighlight]="'#ffe066'" のようにハイライト色を指定します
// 省略した場合は既定の黄色が使われます
@Input() appHighlight: string = '#ffe066';
// ホスト要素の背景色をこのプロパティで制御します
// 初期値は空文字(スタイルなし)です
@HostBinding('style.backgroundColor') bgColor: string = '';
// ホスト要素の文字色をこのプロパティで制御します
@HostBinding('style.color') textColor: string = '';
// マウスが乗ったときにハイライト色を適用します
@HostListener('mouseenter')
onMouseEnter(): void {
this.bgColor = this.appHighlight;
this.textColor = '#333333';
}
// マウスが離れたときにスタイルをリセットします
@HostListener('mouseleave')
onMouseLeave(): void {
this.bgColor = '';
this.textColor = '';
}
}
// app.component.ts
// [appHighlight] でハイライト色を渡します。省略するとデフォルト色が使われます
import { Component } from '@angular/core';
import { HighlightDirective } from './highlight.directive';
@Component({
standalone: true,
imports: [HighlightDirective],
selector: 'app-root',
template: `
<p [appHighlight]="'#c8f7c5'" style="padding:8px;">
マウスを乗せると緑でハイライトされます
</p>
<p [appHighlight]="'#f7c5c5'" style="padding:8px;">
マウスを乗せると赤でハイライトされます
</p>
<p [appHighlight] style="padding:8px;">
デフォルト色(黄色)でハイライトされます
</p>
`,
})
export class AppComponent {}
HTML 属性をバインドする
// disabled-btn.directive.ts
// @HostBinding('attr.disabled') でホスト要素の disabled 属性を制御します
// disabled は DOM プロパティではなく HTML 属性なので attr. プレフィックスを使います
import { Directive, HostBinding, Input, OnChanges, SimpleChanges } from '@angular/core';
@Directive({
standalone: true,
selector: '[appDisabledBtn]',
})
export class DisabledBtnDirective implements OnChanges {
// テンプレート側で [appDisabledBtn]="isLoading" のように制御フラグを渡します
@Input() appDisabledBtn: boolean = false;
// disabled 属性をバインドします
// undefined を渡すと属性が除去され、空文字を渡すと属性が付きます
@HostBinding('attr.disabled') disabledAttr: string | undefined = undefined;
// 入力値が変わるたびに disabled 属性を更新します
ngOnChanges(changes: SimpleChanges): void {
if (changes['appDisabledBtn']) {
// true のときは属性を付与し、false のときは除去します
this.disabledAttr = this.appDisabledBtn ? '' : undefined;
}
}
}
// app.component.ts
// 送信中フラグを使って、ボタンの disabled 状態をディレクティブで管理します
import { Component } from '@angular/core';
import { DisabledBtnDirective } from './disabled-btn.directive';
@Component({
standalone: true,
imports: [DisabledBtnDirective],
selector: 'app-root',
template: `
<button [appDisabledBtn]="isSubmitting" (click)="submit()">
{{ isSubmitting ? '送信中...' : '送信' }}
</button>
<p>送信状態: {{ isSubmitting ? '送信中' : '待機中' }}</p>
`,
})
export class AppComponent {
// フォームの送信中かどうかを管理するフラグです
isSubmitting: boolean = false;
// 送信ボタンが押されたときに呼ばれます
submit(): void {
this.isSubmitting = true;
// 2秒後に送信完了として元の状態に戻します(実際はAPIコールなどを行います)
setTimeout(() => {
this.isSubmitting = false;
}, 2000);
}
}
aria 属性をバインドしてアクセシビリティを高める
// expandable.directive.ts
// @HostBinding('attr.aria-expanded') でアコーディオンの開閉状態を
// スクリーンリーダーに伝える aria-expanded 属性に反映させます
import { Directive, HostBinding, HostListener } from '@angular/core';
@Directive({
standalone: true,
selector: '[appExpandable]',
})
export class ExpandableDirective {
// 開閉状態を管理します
isExpanded: boolean = false;
// aria-expanded 属性に開閉状態を文字列で反映します
// スクリーンリーダーはこの属性を読み上げてユーザーに状態を伝えます
@HostBinding('attr.aria-expanded') get ariaExpanded(): string {
return this.isExpanded ? 'true' : 'false';
}
// aria-controls 属性に固定値を設定します
@HostBinding('attr.aria-controls') ariaControls: string = 'expandable-content';
// クリックで開閉を切り替えます
@HostListener('click')
onClick(): void {
this.isExpanded = !this.isExpanded;
}
}
// app.component.ts
// [appExpandable] を付けたボタンが aria-expanded を自動管理します
import { Component } from '@angular/core';
import { NgIf } from '@angular/common';
import { ExpandableDirective } from './expandable.directive';
@Component({
standalone: true,
imports: [NgIf, ExpandableDirective],
selector: 'app-root',
template: `
<button [appExpandable]="true" style="padding:8px 16px;">
詳細を表示する
</button>
<div id="expandable-content"
*ngIf="expanded"
style="border:1px solid #ccc; padding:12px; margin-top:8px;">
<p>ここに詳細コンテンツが入ります。</p>
</div>
`,
})
export class AppComponent {
// 開閉状態の表示制御はシンプルにコンポーネント側で持ちます
expanded: boolean = false;
}
主なバインディングの種類
| バインディング | 例 | 説明 |
|---|---|---|
| DOM プロパティ | @HostBinding('hidden') | ホスト要素の DOM プロパティを直接バインドします。 |
| CSS クラス | @HostBinding('class.active') | boolean 値に応じてホスト要素に CSS クラスを付与・除去します。 |
| インラインスタイル | @HostBinding('style.color') | ホスト要素のインラインスタイルを文字列でバインドします。 |
| HTML 属性 | @HostBinding('attr.aria-label') | DOM プロパティに存在しない任意の HTML 属性をバインドします。 |
概要
@HostBinding は、ディレクティブやコンポーネントのホスト要素のプロパティ・クラス・スタイル・属性を、クラスのプロパティに紐付けるデコレータです。テンプレートの外からホスト要素を制御できるため、再利用可能なディレクティブの中に見た目の変更をカプセル化するのに適しています。
'class.クラス名' で CSS クラスの付与・除去、'style.プロパティ名' でインラインスタイルの変更、'attr.属性名' で aria-* などの HTML 属性の更新がそれぞれ行えます。ホスト要素のイベントを扱う @HostListener と組み合わせると、テンプレートを変更せずに要素の動作と外観をディレクティブだけで完結させることができます。プロパティバインディングをテンプレート側で行う方法については プロパティバインディング もあわせてご覧ください。
よくあるミス: DOM プロパティと HTML 属性を混同する
disabled のように DOM プロパティとして存在するものは @HostBinding('disabled') で直接バインドできます。一方、aria-expanded などの HTML 属性は DOM プロパティとして存在しないため、attr. プレフィックスが必要です。プレフィックスを間違えると期待どおりに動作しません。
// NG: aria 属性に attr. をつけていない(aria-expanded が正しく設定されない)
@HostBinding('aria-expanded') isExpanded: boolean = false;
// OK: HTML 属性は attr. プレフィックスをつける
@HostBinding('attr.aria-expanded') get ariaExpanded(): string {
return this.isExpanded ? 'true' : 'false';
}
よくあるミス: class. を使わずにクラス名を直接バインドしようとする
CSS クラスを boolean で付与・除去したい場合は @HostBinding('class.クラス名') を使います。@HostBinding('class') だけを使うとクラス属性全体を文字列で上書きしてしまい、他のクラスが消えてしまいます。呪術廻戦の伏黒恵の例で確認します。
// NG: class 属性全体を文字列で上書きしている(他のクラスが消える)
@HostBinding('class') get hostClass(): string {
return this.isActive ? 'active' : '';
}
// OK: class.クラス名 で特定クラスの付与・除去だけを制御する
import { Directive, HostBinding, HostListener, Input } from '@angular/core';
@Directive({
standalone: true,
selector: '[appActiveItem]',
})
export class ActiveItemDirective {
// true のときだけ active クラスが付与される(他のクラスには影響しない)
@HostBinding('class.active') isActive: boolean = false;
@Input() itemName: string = '';
@HostListener('click')
onClick(): void {
this.isActive = !this.isActive;
console.log(this.itemName + ' の選択状態:', this.isActive);
}
}
よくあるミス: undefined と null の扱いの違いを知らない
@HostBinding('attr.属性名') に null または undefined を設定すると属性が除去されます。false(boolean)を設定しても属性は除去されず、文字列 "false" として残るため注意が必要です。
// NG: false を設定しても属性が残る(disabled="false" という属性が付く)
@HostBinding('attr.disabled') disabledAttr: boolean | undefined = false;
// OK: undefined を使って属性を除去する
@HostBinding('attr.disabled') disabledAttr: string | undefined = undefined;
// フラグが true のときは空文字(属性あり)、false のときは undefined(属性なし)
setDisabled(isDisabled: boolean): void {
this.disabledAttr = isDisabled ? '' : undefined;
}
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。