タスクを更新する
2015/07/21
前回は、タスクのタイトル(件名)を修正するフォームを表示しました。
今回は、そのフォームを使ってデータベースを更新する機能を実装します。
本題に入る前に、UI 上の工夫として次のような仕様を取り入れます。
- 初期状態では新規追加フォームを表示し、編集フォームは表示しない。
- 「Edit」ボタンがクリックされると、新規追加フォームを消して、編集フォームを表示する。
reset()
メソッド呼び出しで初期状態に戻す。
どのように TodoList
クラスを書き換えればいいでしょうか。演習問題として考えてみてください。
2つの実装例を紹介します。
第1の例では render()
メソッドを次のように変更します。
render(m) {
m.ul(m => {
this.ds.tasks.forEach(task => {
m.li(m => this.renderTask(m, task));
});
});
if (this.editingTask) this.renderUpdateForm(m);
else this.renderCreateForm(m);
}
this.renderUpdateForm(m)
と this.rendercreateForm(m)
の順序が入れ替わっている点に注意してください。
ただし、この実装例は Cape.JS 1.1.2 以上でないとうまく動きません。bower.json
を次のように書き換えて bower install
コマンドを実行してください。
...
"dependencies": {
"capejs": "~1.1.2",
"bootstrap": "=3.3.4",
"fontawesome": "~4.3.0",
"lodash": "~3.9.3"
}
}
bootstrap
のバージョンが =3.3.4
である点に留意してください。2015年8月1日現在の最新版である 3.3.5
ではうまく動作しません。詳しくは、Stack Overflow に書いた私の質問と回答をご覧ください。
第2の実装例では、renderCreateForm()
メソッドと renderUpdateForm()
メソッドを次のように変更します。
renderCreateForm(m) {
m.formFor('new_task', { visible: !this.editingTask }, m => {
m.onkeyup(e => this.refresh());
m.textField('title').sp();
m.attr({ disabled: this.val('new_task.title').trim() === '' });
m.onclick(e =>
this.ds.createTask(this.val('new_task.title', '')));
m.btn(`Add task #${ this.ds.tasks.length + 1 }`);
});
}
renderUpdateForm(m) {
m.formFor('task', { visible: !!this.editingTask }, 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() });
});
}
それぞれメソッド定義の内側1行目で formFor()
メソッドの第2引数に visible
オプションを指定しています。このオプションに false
を与えると <form>
要素全体が非表示(display: none
)になります。
見え方は同じですが、これら2つの実装例は大きく異なります。前者では1個の <form>
要素が作られるだけですが、後者では2個の <form>
要素が作られます。
本稿では前者を採用したと仮定して次に進みます。
さて、Rails アプリケーション側の API はすでにできています。app/controllers/api/tasks_controller.rb
に次のような update
アクションが実装済みです。
def update
task = Task.find(params[:id])
if task.update_attributes(task_params)
render text: 'OK'
else
render text: 'NG'
end
end
6月14日の「Ajax によるデータの更新」で作りました。
ですから、ここからは割と簡単です。
まず、TaskStore
クラスに updateTask()
メソッドを追加します。
updateTask(task, title) {
$.ajax({
type: 'PATCH',
url: '/api/tasks/' + task.id,
data: { task: { title: title } }
}).done(data => {
if (data === 'OK') {
task.title = title;
this.propagate();
}
});
}
createTask()
メソッドの次に挿入してください。
続いて、TodoList
クラスの renderUpdateForm()
メソッドを次のように書き換えます(下から4行目)。
renderUpdateForm(m) {
m.formFor('task', { visible: !!this.editingTask }, m => {
m.onkeyup(e => this.refresh());
m.textField('title').sp();
m.attr({ disabled: this.val('task.title').trim() === '' });
m.btn('Update', { onclick: e => this.updateTask() });
m.btn('Cancel', { onclick: e => this.reset() });
});
}
そして、TodoList
クラスに updateTask()
メソッドを次のように実装します。
updateTask() {
var task = this.editingTask;
task.modifying = false;
this.editingTask = null;
this.ds.updateTask(task, this.val('edit.title', ''));
}
ブラウザで動作確認してください。「借りた本を返す」の右の「Edit」ボタンをクリックし、末尾に句点「。」を加えて「Update」ボタンをクリックし、タスクの件名が更新されればOKです。
次回は、タスクの削除機能を実装する予定です。どのような実装になるでしょうか。
読者の皆さんも演習問題として実装法を考えてみてくださいね。