Dev Study
← 解説「host — 自分自身の要素へバインドする」に戻る

サンプルコードで身につける: host — 自分自身の要素へバインドする

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1選択状態をホスト要素のクラスに反映する

host の '[class.selected]' でホスト要素にクラスを条件付きで付ける、いちばん基本の形です。テンプレート内の要素ではなく <app-plan-card> タグ自身にクラスが付くので、:host.selected でスタイルを当てます。

import { Component, input } from '@angular/core';

@Component({
  selector: 'app-plan-card',
  // 条件が true のとき <app-plan-card> に selected クラスが付く
  host: {
    '[class.selected]': 'isSelected()',
  },
  template: '<p>スタンダードプラン</p>',
  styles: [':host.selected { border: 2px solid teal; }'],
})
export class PlanCardComponent {
  isSelected = input(false);
}

2部品全体をクリックできるカードにする

host の '(click)' で、部品のどこを押してもメソッドが呼ばれるカードを作る例です。テンプレート内のボタンだけでなくタグ全体が反応するので、リンクカードのようなUIに向いています。

import { Component, input, output } from '@angular/core';

@Component({
  selector: 'app-article-card',
  // 部品のどこをクリックしても opened が発火する
  host: {
    '(click)': 'opened.emit(articleId())',
  },
  template: '<h3>{{ title() }}</h3><p>続きを読む…</p>',
  styles: [':host { display: block; cursor: pointer; }'],
})
export class ArticleCardComponent {
  articleId = input.required<number>();
  title = input.required<string>();
  opened = output<number>();
}

3属性とスタイルもバインドできる

host では class だけでなく '[attr.aria-expanded]' のような属性や '[style.opacity]' のようなスタイルもバインドできます。開閉状態をアクセシビリティ属性へ自動反映するなど、部品の状態と見た目・属性を1か所で結び付けられます。

import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-accordion-item',
  // 属性(attr.)もスタイル(style.)もホストへバインドできる
  host: {
    '[attr.aria-expanded]': 'isOpen()',
    '[style.opacity]': 'isOpen() ? 1 : 0.6',
    '(click)': 'toggle()',
  },
  template: '<p>クリックで開閉</p>',
})
export class AccordionItemComponent {
  isOpen = signal(false);

  toggle() {
    this.isOpen.update((v) => !v);
  }
}

4ディレクティブの host で相手の要素に作用する

host はディレクティブでも使えます。この場合バインド先は「ディレクティブが付けられた相手の要素」で、状態に応じたクラスの付け外しをクラスひとつで完結できます。ElementRef で直接スタイルを触るより宣言的に書けるのが利点です。

import { Directive, signal } from '@angular/core';

@Directive({
  selector: '[appPressable]',
  // 付けられた要素(ボタンなど)に押下中クラスを付ける
  host: {
    '[class.pressing]': 'pressing()',
    '(mousedown)': 'pressing.set(true)',
    '(mouseup)': 'pressing.set(false)',
    '(mouseleave)': 'pressing.set(false)',
  },
})
export class PressableDirective {
  pressing = signal(false);
}

// 使う側: <button appPressable>押している間だけ凹む</button>
// CSS側に .pressing { transform: scale(0.97); } などを用意する

5キーボード操作をホストで受け取る

'(keydown.escape)' のようにキー名まで指定したイベントも host に書けます。ESCキーで閉じるパネルのようなキーボード対応が宣言的に書け、tabindex を属性として持たせればフォーカスを受け取れる要素になります。

import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-side-panel',
  // ESCキーが押されたら閉じる。tabindex でフォーカス可能にする
  host: {
    'tabindex': '0',
    '(keydown.escape)': 'close()',
  },
  template:
    '@if (isOpen()) {' +
    '  <div>パネルの中身(ESCで閉じる)</div>' +
    '}',
})
export class SidePanelComponent {
  isOpen = signal(true);

  close() {
    this.isOpen.set(false);
  }
}