部分コンポーネント(1)

2015/11/19

Matryoshka doll

連載再開後の最初のテーマは、部分コンポーネント(partials)です。

Ruby on Rails の「部分テンプレート(partials)」と同様に、ビジュアルデザイン要素を入れ子にするための仕組みです。

部分テンプレートを用いると、肥大化した Cape.JS コンポーネントのソースコードを分割して読みやすく書き換えることができます。

「Cape.JS 入門」で作ってきた TODO アプリのソースコードの整理整頓を行いましょう。


「Cape.JS 入門」終了時点でのソースコードは

https://github.com/oiax/capejs-tut2-app-source/archive/lecture-00.zip

から取得できます。

あなたの PC 上でこの ZIP ファイルを展開し、以下のコマンドを順に実行してください。

$ bundle
$ bower install
$ bin/rake db:setup
$ bin/rails s

この TODO アプリを動作させるためには、Ruby 2.2、Bundler、Node.js、Bower などのソフトウェアがインストール済みである必要があります。

ブラウザで http://localhost:3000 を開くと、次のような画面になります。

Screen Capture

タスクの追加・編集・削除ができるかどうか、タスクを「済み」に変えられるかどうか、タスクの表示順を入れ替えられるかどうか、確かめてください。

「Cape.JS 入門」ではタスクのタイトルに日本語を使用してきましたが、「続 Cape.JS 入門」では英語にしました。将来的に英訳をする際の手間を減らすためです。


次に、準備作業として各種ライブラリを最新のバージョンに更新します。

まず、Gemfile を開いて rails のバージョン番号を 4.2.4 から 4.2.5 に、sprockets-es6 のバージョン番号を 0.7.0 から 0.8.0 に書き換えます。そしてターミナルで bundle update を実行してください。

続いて、bower.json を開いて capejs のバージョン番号を 1.2.0 から 1.3.0 に、bootstrap のバージョン番号を =3.3.4 から ~3.3.5 に書き換えます。そしてターミナルで bower update を実行してください。

「部分コンポーネント」は Cape.JS 1.3 の新機能です。


さて、部分コンポーネントについて簡単に原理を説明しましょう。次のソースコードをご覧ください。

class HelloMessage extends Cape.Component {
  init() {
    this.name = 'world';
  }

  render(m) {
    m.p(m => {
      m.text('Hello, ');
      m.strong(m => this.name + '!')
    })
  }
}

このコンポーネントは次のような HTML の断片を生成します。

<p>Hello, <strong>world!</strong></p>

ここで部分コンポーネント NamePresenter を導入します。

class NamePresenter extends Cape.Partial {
  render(m) {
    m.strong(m => this.parent.name + '!')
  }
}

3 行目の this.parent はこの部分コンポーネントを包含する親コンポーネントを参照していますので、もしこの部分コンポーネントがコンポーネント HelloMessage の「子」になるのであれば、this.parent.name で "world" という文字列を取得できます。

そこで、コンポーネント HelloMessage を次のように書き換えることができます。

class HelloMessage extends Cape.Component {
  init() {
    this.name = 'world';
    this.namePresenter = new NamePresenter(this);
  }

  render(m) {
    m.p(m => {
      m.text('Hello, ');
      this.namePresenter.render(m);
    })
  }
}

この変更により <strong>world!</strong> という HTML の断片を生成するコードが、部分コンポーネントに抜き出されました。


次に、部分コンポーネントの実装でよく使われる技法を紹介します。いま、NamePresenter クラスのソースコードはこうなっています。

class NamePresenter extends Cape.Partial {
  render(m) {
    m.strong(m => this.parent.name + '!')
  }
}

3 行目の this.parent.name という部分に着目してください。NamePresenter は「名前」を描画するための部分コンポーネントなのですが、肝心の「名前」のデータをそれ自身は所持していないため、このような間接的な書き方をしなければなりません。この例では「名前」のデータが必要になるのが 1 回だけなのでそれほど気になりませんが、このデータを繰り返し参照するのなら this.parent.name ではなく this.name と書きたいところです。

そこで、次のように書き換えることにしましょう。

class NamePresenter extends Cape.Partial {
  constructor(parent) {
    super(parent);
    this.name = parent.name;
  }

  render(m) {
    m.strong(m => this.name + '!')
  }
}

スーパークラス Cape.Partial のコンストラクタをオーバーライドしています。super は ECMAScript6 で導入された新しいキーワードで、スーパークラスのコンストラクタを呼び出します。


部分コンポーネントを利用する上で注意しなければならないのは、以下のメソッドが親コンポーネントの同名メソッドに委譲されている点です。

  • checkedOn()
  • formData()
  • jsonFor()
  • paramsFor()
  • refresh()
  • val()
  • valuesFor()

例えば、部分コンポーネントの refresh() メソッドを呼び出すと、実際には親コンポーネントの refresh() メソッドが呼び出されます。その結果として親コンポーネントも部分コンポーネントも再描画されることになります。

部分コンポーネントが別の部分コンポーネントを包含している場合、つまり二重の入れ子になっている場合、孫コンポーネントの refresh() メソッド呼び出しはその親の親の refresh() メソッドを呼び出すことになります。


今回はここまでとしましょう。

次回は部分コンポーネントの仕組みを用いて、実際に TODO アプリケーションのリファクタリングを行います。