2020年12月30日水曜日

Angular・Componentのタグにclassを付与したい【HostBinding】


こんにちは、オフィス狛 技術部のpinoです。

早速ですが、皆さんはAngularをやっていて「コンポーネントのタグに直接classを付与したいんだけどなぁ....。」と思った経験はないでしょうか。

今回は、そんな時に使える @HostBinding についてご紹介したいと思います!

準備

まずは、コンポーネントを用意します。
app.component.html(親コンポーネント)
<header class="header">
  <section class="header__about">
    <h1 class="header__title">@HostBinding Sample</h1>
    <p class="header__lead">動作確認用のサンプルです</p>
  </section>
  <nav class="header-nav">
    <ul class="header-nav__list">
      <app-list-item *ngFor="let name of names" [name]="name"></app-list-item>
    </ul>
  </nav>
</header>

list-item.component.html
<li class="header-nav__item">
  <input type="checkbox" />{{ name }}
</li>
list-item.component.css
.header-nav__item {
  width: 120px;
  height: 64px;
  padding-left: 32px;
  line-height: 64px;
}
list-item.component.ts
import { Component, Input, OnInit } from '@angular/core';

@Component({
  selector: 'app-list-item',
  templateUrl: './list-item.component.html',
  styleUrls: ['./list-item.component.scss']
})
export class ListItemComponent implements OnInit {
  @Input() name: string;

  constructor() { }

  ngOnInit(): void {
  }
}
ビルド後のイメージ

色味がなく、シンプルで寂しい印象です。
ここから、実際にHostBindingを使ってclassを付与してみたいと思います。

HostBindingの使い方

使い方は

1. コンポーネントのタグにclassを付与する
2. 上記1に加えて、動的にclassの付け外しをする

と、主に2つあります。

まずは、1. コンポーネントのタグにclassを付与するについてです。
こちらは、大きく2通りの書き方ができます。
list-item.component.css
import { Component, HostBinding, Input, OnInit } from '@angular/core';  // HostBinding を追加

@Component({
  selector: 'app-list-item',
  templateUrl: './list-item.component.html',
  styleUrls: ['./list-item.component.scss']
})
export class ListItemComponent implements OnInit {
  @HostBinding('class') listItem = 'list-item';  // ①
  @HostBinding('class.first') first = true;  // ②

  @Input() name: string;

  constructor() { }

  ngOnInit(): void {
  }
}

ポイントは、@HostBinding()の、()の中です。
①は、「'class'」のみ指定して、なんのclassを付与するかは変数の値が使用されるパターンです。
値部分に定数を使えば、ロジック側にテンプレートの情報をベタ書きすることも避けられますね。
固定のclassであればこれで十分そうです。

②は、「'class.first'」と付与するclass名まで指定するパターンです。
この場合は、後に続いている値がtrueであれば付与、falseであれば付与しない、ということになります。
(※真偽値を固定で設定したい場合は、「@HostBinding('class.first') readonly first = true;」 といった具合に、readonlyをつけるなどの対策が考えられます。)

また、HostBindingの話とはすこし逸れますが、コンポーネントのタグに使用するclassは
グローバルである(style.cssに記述されている等)か、:hostメタデータを使用する必要があります。

今回は、自コンポーネントのCSSに背景色をつけるclassを用意したいと思います。
list-item.component.css
// 以下を追加
:host.list-item.first {
  background-color: #7ae5ec;
}

:host.list-item.second {
  background-color: #7aecc0;
}

:host.list-item.third {
  background-color: #7ab9ec;
}
ここまでのビルド後イメージ

すべてのコンポーネントにclassが付与されて、背景色がつきました。


次は、2. 上記1に加えて、動的にclassの付け外しをするについてです。
list-item.component.css
export class ListItemComponent implements OnInit {
  @HostBinding('class')
  @Input() name: string; // ①
  
  // 今回formは用意していないので、例としてだけ挙げます
  @HostBinding('class.valid') get valid() {
    return this.form.valid;
  } // ②

  @HostBinding('class.list-item') isClick = false; // ③-A
  ...
  onClick(): void {
    this.isClick = !this.isClick;
  } // ③-B

他にもいろいろな方法があると思うのですが、簡単に思いつくものを挙げてみました。

①は、Input()で受け取った値を使用します。
組み合わせて使うだけで、特別なことをしてる感じがします...!

②について、今回Formは用意していないので記述の例になるのですが、状態など値そのものを監視したい時に使える方法です。

getterってあまり使う機会がなかったのですが、今回試してみたところ使い勝手がいいですね!


また③は、②のようにイベント発生時ではなく、識別子に設定した変数の値を書き換えることによってclassの付け外しをしています。

ここまでで、上記の①と③をプログラムに落とし込んだビルド後のイメージが以下のようになります。

それぞれで別々のclassの付与と、classの付け替えができました!

補足

ここまでclassを付与する使い方を紹介してきましたが、なんとHostBindingはclass以外のバインディングにも使えます。
例えば、「@HostBinding('style.display') flex = 'flex';」としてあげればstyle="display: flex;"がバインドされます。
便利!

今回は公式以外に以下サイトも参考にしたのですが、「HostBinding('value') myValue;は[value]="myValue"と全く同じです。」という一文が特にわかりやすかったです。
@HostBindingと@HostListener:彼らは何をし、何のためにいますか?


以上、HostBindingデコレータについて紹介しました。
参考になればうれしいです。

0 件のコメント:

コメントを投稿