タスクの編集フォーム

2015/07/16

前回はTodoListのタスクを新規作成する機能を作りました。

今回は、タスクのタイトル(件名)を修正するフォームを表示します。


JavaScript プログラムに手を付ける前に、先回りして CSS を書き換えておきます。

#todo-list {
  label.completed span {
    color: #888;
    text-decoration: line-through;
  }
  button[disabled] {
    color: #888;
  }
  button + button {
    margin-left: 4px;
  }
  span.modifying {
    font-weight: bold;
    color: #800;
  }
  span.button {
    cursor: pointer;
    background-color: #888;
    color: #fff;
    margin-left: 4px;
    padding: 4px 8px;
    font-size: 60%;
  }
}

button + button { の行から下(最終行を除く)を挿入してください。


では、始めましょう。todo_list.es6 を開いてください。まず、TodoList クラスの init() メソッドを次のように書き換えます(4行目を追加)。

  init() {
    this.ds = new TaskStore();
    this.ds.attach(this);
    this.editingTask = null;
    this.ds.refresh();
  }

editingTask という名前の属性に null をセットしています。後で見るように、この属性は編集中のタスク(オブジェクト)を保持することになります。

次に、render() メソッドを次のように変更します(下から2行目を挿入)。

  render(m) {
    m.ul(m => {
      this.ds.tasks.forEach(task => {
        m.li(m => this.renderTask(m, task));
      });
    });
    this.renderCreateForm(m);
    this.renderUpdateForm(m);
  }

さらに、renderTask() メソッドを修正(6行目、9-10行目を挿入)。

  renderTask(m, task) {
    m.class({ completed: task.done });
    m.label(m => {
      m.onclick(e => this.ds.toggleTask(task));
      m.input({ type: 'checkbox', checked: task.done }).sp();
      m.class({ modifying: task.modifying });
      m.span(task.title);
    });
    m.onclick(e => this.editTask(task));
    m.span('Edit', { class: 'button' });
  }

既存の renderCreateForm() メソッドの下に以下の2個のメソッドを追加します。

  renderUpdateForm(m) {
    m.formFor('task', m => {
      m.onkeyup(e => this.refresh());
      m.textField('title').sp();
      m.attr({ disabled: this.val('task.title').trim() === '' });
      m.btn('Update');
    });
  }

  editTask(task) {
    if (this.editingTask) this.editingTask.modifying = false;
    task.modifying = true;
    this.editingTask = task;
    this.val('task.title', task.title);
    this.refresh();
  }

各タスクのタイトルの右に表示される「Edit」ボタン(<span> 要素)をクリックすると editTask() メソッドが呼び出されるようにしました。

このメソッドは編集したいタスク(task)の「修正中フラグ(modifying)」を true にし、それを TodoList コンポーネントの editingTask 属性にセットします。そして、その title 属性の値を編集フォームの title フィールドに入れて TodoList コンポーネントを再描画します。


では、ブラウザで動作確認をしましょう。まず、初期画面はこうなります。

画面キャプチャ

「猫のえさを買う。」の右の「Edit」ボタンをクリックすると、こう変化します。

画面キャプチャ

ラベルが赤くなったのは、このタスクに「修正中フラグ(modifying)」が付いたためです。renderTask() メソッドの中に次のような記述があります。

      m.class({ modifying: task.modifying });
      m.span(task.title);

この結果、<span class="modifying">猫のえさを買う。</span> のような HTML タグが生成され、本稿冒頭で書き換えた CSS の効果で色が赤くなるのです。


UI をもう少し改善しましょう。タスクの編集をキャンセルできるようにします。renderUpdateForm() メソッドを次のように書き換えます(下から3行目を追加)。

  renderUpdateForm(m) {
    m.formFor('task', m => {
      m.onkeyup(e => this.refresh());
      m.textField('title').sp();
      m.attr({ disabled: this.val('task.title').trim() === '' });
      m.btn('Update');
      m.btn('Cancel', { onclick: e => this.reset() });
    });
  }

そして、editTask() メソッドの下に reset() メソッドを追加します。

  reset() {
    if (this.editingTask) this.editingTask.modifying = false;
    this.editingTask = null;
    this.val('task.title', '');
    this.refresh();
  }

このメソッドは、編集中のタスクの「修正中フラグ(modifying)」を false にしたり、編集フォームの title フィールドを空にしたりします。

ブラウザの表示は次のようになります。

画面キャプチャ

適当な「Edit」ボタンをクリックして「Cancel」ボタンをクリックすると、初期画面に戻ります。


UI の改善を続けます。同じタスクの横の「Edit」ボタンを2回連続してクリックしたとき、そのタスクの編集がキャンセルされるようにしましょう。

editTask() メソッドを次のように書き換えてください。

  editTask(task) {
    if (this.editingTask === task) {
      this.reset();
    }
    else {
      if (this.editingTask) this.editingTask.modifying = false;
      task.modifying = true;
      this.editingTask = task;
      this.val('task.title', task.title);
      this.refresh();
    }
  }

ブラウザで動作確認してください。「猫のえさを買う。」の右の「Edit」ボタンを2回連続してクリックして、画面が初期状態に戻れば OK です。


今回はここまで。

次回は、このフォームから Ajax リクエストでデータベースを更新する処理を実装します。