部分コンポーネント(1)
2015/11/19
連載再開後の最初のテーマは、部分コンポーネント(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
を開くと、次のような画面になります。
タスクの追加・編集・削除ができるかどうか、タスクを「済み」に変えられるかどうか、タスクの表示順を入れ替えられるかどうか、確かめてください。
「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 アプリケーションのリファクタリングを行います。