SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

再利用性とカプセル化のためのWeb Componentsを基礎から学ぶ

Web Componentsを簡便に扱う「Lit」の実践的な使い方を学ぶ──ToDoリストを作ろう

再利用性とカプセル化のためのWeb Componentsを基礎から学ぶ 第4回

  • X ポスト
  • このエントリーをはてなブックマークに追加

ベースを作成する

 まずはベースとなるHTMLとカスタム要素のクラスを作成しておきましょう。まずはHTMLです(リスト1)。

[リスト1]todolist.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script type="module" src="./todolist.js"></script>
  <title>Litで作るToDoリスト</title>
</head>
<body>
  <todo-list></todo-list>
</body>
</html>

 HTML側では todolist.js を読み込んで <todo-list> を使用していますね。こちらの定義を todolist.js に作成します(リスト2)。

[リスト2]todolist.js
import {LitElement, html} from 'https://cdn.jsdelivr.net/gh/lit/dist@2/all/lit-all.min.js';

class ToDoListElement extends LitElement {
  static properties = {};

  render() {
    return html`
      <h1>ToDoリスト</h1>
      <li><label><input type="checkbox" />タスク1</label></li>
      <li><label><input type="checkbox" />タスク2</label></li>
      <li><label><input type="checkbox" />タスク3</label></li>
    `;
  }
}

customElements.define('todo-list', ToDoListElement);

 イメージを掴みやすくするために、チェックリストを仮置きしました。

図2:チェックリストを仮置きした
図2:チェックリストを仮置きした

 これを元に、ToDoリストを作っていきます。

タスクのリストを表示する

 まずは、配列データを元にチェックリストを生成できるようにしましょう(リスト3)。

[リスト3]todolist.js
// (略)
class ToDoListElement extends LitElement {
  static properties = {
    tasks: {
      state: true, // (1)
    },
  };

  constructor() {
    super();
    // (2)
    this.tasks = [
      { id: 1, label: 'カプセル化と再利用性について学ぶ', completed: true },
      { id: 2, label: 'Web ComponentsのAPIを学ぶ', completed: true },
      { id: 3, label: 'Litの基本的なAPIを学ぶ', completed: true },
      { id: 4, label: 'Litの実践的な使い方を学ぶ', completed: false },
    ];
  }

  render() {
    return html`
      <h1>ToDoリスト</h1>
      ${this.tasks.map((task) => { // (3)
        return html`
          <li>
            <label>
              <input type="checkbox" ?checked=${task.completed} /><!-- (4) -->
              ${task.label}
            </label>
          </li>
        `;
      })}
    `;
  }
}

 まず、配列データはクラスの内部プロパティ(属性として公開しないプロパティ)として tasks という名前で定義したいので、(1)で tasks の宣言と、 state: true オプションの設定を行いました。次に、コンストラクタのタイミングで、(2)のように配列データを初期化します。そして、データの用意ができたら、 render() 内のテンプレートに反映していきます。最も簡単な方法は、(3)のように Array.map() 関数でデータをテンプレートの配列に変換することです。これで配列データをテンプレート内に並べられます。最後に、データ内の completed プロパティを使ってチェック状態を設定したいので、(4)のように ?checked=${task.completed}チェックボックスのchecked属性に値を設定しています。

 リスト3を表示すると、図3のようになります。

図3:配列データからリストを生成する
図3:配列データからリストを生成する

 tasks に定義したデータのうち、 completed: true になっているものだけにチェックがついています。どうやら上手くいったようです。

 ?checked の先頭に ? の文字がついているのが気になりますね。これはLit特有のテンプレート記法の一つで、boolean型の属性を記述するときに属性名の頭に付けるプレフィックスです。他にも、カスタム要素のプロパティにアクセスするためのプレフィックスとして . があったり( .value=${value} のように使用)、イベントリスナーを登録するためのプレフィックスとして @ があります( @click=${() => this.onClick()} のように使用)。

ディレクティブを用いて短く書く

 リストを表示する際、 Array.map() を使う方法でも問題ないのですが、テンプレート内にJavaScriptを書きすぎると文書構造の見通しが悪くなるという問題があります。そのため、Litはテンプレート内で頻出する各種処理を簡潔に記述するための、ディレクティブという関数群を用意しています。

 例えば、ループや条件分岐に関するディレクティブとしては表1のようなものがあります。

表1:ループや条件分岐に関するディレクティブ
ディレクティブ名 概要
when 第一引数のtruthy/falthyに応じて表示を切り替える(三項演算子の代替)
choose 条件にマッチしたものを表示する(switch-case文の代替)
map Iterableなデータをリストに変換する(for-of文の代替)
repeat mapとほぼ同じ機能で、より強力にデータとDOMを紐付けて並び替えに強くする
join テンプレートのリストの間に別のテンプレートを挿入する( Array.join() の代替)
range 特定の範囲の整数の配列を生成する
ifDefined 属性の定義時に用い、引数のデータがnullかundefinedの場合に属性の定義自体を削除する

 render() メソッド内がごちゃごちゃしそうな頻出処理がスッキリしそうなものばかりです。

 今回のサンプルでは map() を使ってみましょう(リスト4)。

[リスト4]todolist.js
// (1) mapを追加
import {LitElement, html, map} from 'https://cdn.jsdelivr.net/gh/lit/dist@2/all/lit-all.min.js';
// 略
  render() {
    return html`
      <h1>ToDoリスト</h1>
      <!-- (2) mapに置き換え -->
      ${map(this.tasks, (task) => {
        return html`
          <li>
            <label>
              <input type="checkbox" ?checked=${task.completed} />
              ${task.label}
            </label>
          </li>
        `;
      })}
    `;
  }
// 略

 map() を使用するには、(1)のようにLitライブラリからインポートします。使い方は(2)のように第一引数にIterableオブジェクト、第二引数にテンプレートを記述します。この例では Array.map() とさほど変わりませんが、ディレクティブの map() は第一引数に配列以外のIterable型オブジェクトも取れるので、幅広いデータで活用できます。

次のページ
チェック状態の変更をハンドリングしてスタイルを変更する

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
再利用性とカプセル化のためのWeb Componentsを基礎から学ぶ連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 中川幸哉(ナカガワユキヤ)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/17136 2023/01/31 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング