コンポーネントにデータを持たせる

2015/06/07

前回は、Cape.JS を Ruby on Rails に組み込む手順を解説しました。

今回のテーマは、コンポーネントとデータの関係です。


私たちの目標は、簡単な Web アプリケーション「Todo リスト」の開発です。このアプリケーションは、「タスク(やらなくてはいけないこと)」の集合を扱います。このようなデータはオブジェクトの配列として表現するのがいいでしょう。

ここでいう「オブジェクト」とは、JavaScript のオブジェクトのことです。JavaScript のオブジェクトは、Ruby のハッシュ(連想配列)の性質を持っています。例えば、obj = { x: 1, y: 2 }; obj["z"] = 3 のように書けば、3個のプロパティと値のペアを持つオブジェクト obj ができます。obj["z"]obj.z とも書けます。

個々のタスクを例えば次のようなオブジェクトとして表すことにします:

{ title: "猫のえさを買う。", done: false }

そして、オブジェクトの配列が「タスクリスト」になります。

[
  { title: "猫のえさを買う。", done: false },
  { title: "歯医者に行く。", done: true }
]

ところで、本題に入る前に Cape.JS をアップデートする方法を紹介しておきましょう。

本日(2015/06/07)、Cape.JS 1.1.0 がリリースされました。なるべく最新のものを使うべきなので、「Todo リスト」の Cape.JS も上げておきましょう。

テキストエディタで bower.json を開き、

    "capejs": "~1.0.0",

を次のように書き換えます。

    "capejs": "~1.1.0",

そして、ターミナルで bower install を実行します。


では、Cape.JS のコンポーネントにタスクの配列を持たせてみましょう。テキストエディタで app/assets/javascripts/todo_list.es6 を次のように書き換えてください。

class TodoList extends Cape.Component {
  init() {
    this.tasks = [
      { title: "猫のえさを買う。", done: false },
      { title: "歯医者に行く。", done: true }
    ];
    this.refresh();
  }

  render(m) {
    m.ul(m => {
      this.tasks.forEach(task => {
        m.li(m => {
          m.label(m => {
            m.input({ type: 'checkbox', checked: task.done }).sp();
            m.span(task.title);
          });
        });
      });
    });
  }
}

コンポーネントクラスに init という名前のインスタンスメソッドを定義すると、コンポーネントがマウントされた直後にこのメソッドが実行されます。

ここでは tasks というプロパティにタスクの配列をセットしています。メソッドの末尾で refresh メソッドを呼び出しています。このメソッドはコンポーネントに対して再描画を指示します。その結果、render メソッドが呼ばれて、ブラウザの画面が書き換わります。コンポーネントに init メソッドが存在しない場合は、暗黙裏に this.refresh() が呼ばれますが、init メソッドを定義した場合には、その中で this.refresh() を呼ばないとコンポーネントをマウントしてもブラウザの画面に何も現れませんので注意してください。

Cape.JS は init メソッドの後で常に this.refresh() を呼び出さないのでしょうか。それは、init メソッドの中で Ajax コールが行われてデータの初期化が行われることを想定しているからです。Ajax コールはその名の通り「非同期(synchronous)」ですから、それが完了するのは init メソッドの実行が終わった後になります。したがって、init メソッドの後で this.refresh() を呼び出しても肝心のデータが整っていないことになってしまうのです。この辺りの事情については、この連載が進むにつれて次第に明らかになるでしょう。

render メソッドの中身も大きく変化しています。配列 this.tasks に対して forEach メソッドを呼び出して、ループ処理を行っています。パラメータ task には { title: "猫のえさを買う。", done: false } のようなオブジェクトが順にセットされます。そして、この task の値を参照してチェックボックスとテキストを生成します。

            m.input({ type: 'checkbox', checked: task.done }).sp();
            m.span(task.title);

JavaScript では task["title"] の代わりに task.title と書ける点に注意してください。

では、うまく動くかどうか試してみましょう。ブラウザをリロードして、次のような画面が表示されれば OK です。

画面キャプチャ


だいぶアプリケーションらしくなってきましたね。今回はここまで。次回は、Ajax を利用してコンポーネントのデータを初期化する方法について解説します。