部分コンポーネント(3)
2015/11/21
前回に引き続き、「部分コンポーネント」による TODO アプリのリファクタリングを行っていきます。
まず、app/assets/javascripts/
ディレクトリに新規ファイル update_form.es6
を作成し、テキストエディタで次のような内容を書き込みます。
class UpdateForm extends Cape.Partial {
render(m) {
}
}
そして、todo_list.es6
から renderUpdateForm
メソッドの中身をコピーして、update_form.es6
の render()
メソッドの中身として貼り付けます。
class UpdateForm extends Cape.Partial {
render(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', { onclick: e => this.updateTask() });
m.btn('Cancel', { onclick: e => this.reset() });
});
}
}
todo_list.es6
から renderUpdateForm
メソッド自体は不要ですので削除します。
現在、TodoList
コンポーネントの init()
メソッドはこのように定義されています。
init() {
this.agent = new TaskCollectionAgent(this);
this.createForm = new CreateForm(this);
this.editingTask = null;
this.agent.refresh();
}
これを次のように書き換えてください(4 行目を挿入)。
init() {
this.agent = new TaskCollectionAgent(this);
this.createForm = new CreateForm(this);
this.updateForm = new UpdateForm(this);
this.editingTask = null;
this.agent.refresh();
}
また、TodoList#render()
メソッドは現状こうなっています。
render(m) {
m.ul(m => {
this.agent.objects.forEach((task, index) => {
m.li(m => {
this.renderTask(m, task);
this.renderButtons(m, task, index);
});
});
});
if (this.editingTask) this.renderUpdateForm(m);
else this.createForm.render(m);
}
下から 3 行目を次のように書き換えてください。
if (this.editingTask) this.updateForm.render(m);
ここまでの変更により、TodoList
コンポーネントの renderUpdateForm()
メソッドを部分コンポーネントに抜き出したことになります。
さて、TodoList
コンポーネントに残っているメソッドのうち、以下のメソッドはタスクの編集フォームと強く結びついています。
editTask()
reset()
updateTask()
これらを部分コンポーネント UpdateForm
へ移設してください。結果として、UpdateForm
クラスのソースコードはこうなります。
class UpdateForm extends Cape.Partial {
render(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', { onclick: e => this.updateTask() });
m.btn('Cancel', { onclick: e => this.reset() });
});
}
editTask(task) {
if (this.editingTask === task) {
this.reset();
}
else {
if (this.editingTask) this.editingTask.modifying = false;
task.modifying = true;
this.reset();
this.editingTask = task;
this.val('task.title', task.title);
this.refresh();
}
}
reset() {
if (this.editingTask) this.editingTask.modifying = false;
this.editingTask = null;
this.val('task.title', '');
this.refresh();
}
updateTask() {
var task = this.editingTask;
task.modifying = false;
this.editingTask = null;
this.agent.updateTask(task, this.val('task.title', ''));
}
}
移設したコードを注意深く観察してください。未定義の属性が 2 個存在します。agent
とeditingTask
です。
そこで、CreateForm
クラスの場合と同様にコンストラクタをオーバーライドします。
class UpdateForm extends Cape.Partial {
constructor(parent) {
super(parent);
this.agent = parent.agent;
this.editingTask = null;
}
render(m) {
(以下省略)
agent
属性については親コンポーネントと共有します。「編集中のタスク」を保持するための editingTask
属性は親コンポーネントにあるよりも、UpdateForm
クラスのものとした方がいいでしょう。
TodoList
クラスに残ったメソッドに必要な修正を行います。
まずは、init()
メソッド。
init() {
this.agent = new TaskCollectionAgent(this);
this.createForm = new CreateForm(this);
this.updateForm = new UpdateForm(this);
this.agent.refresh();
}
4 行目の下にあった this.editingTask = null;
を削除しました。
次に、render()
メソッド。
render(m) {
m.ul(m => {
this.agent.objects.forEach((task, index) => {
m.li(m => {
this.renderTask(m, task);
this.renderButtons(m, task, index);
});
});
});
if (this.updateForm.editingTask) this.updateForm.render(m);
else this.createForm.render(m);
}
下から 3 行目の if
文の条件式を this.editingTask
から this.updateForm.editingTask
に変更しました。
最後に、renderButtons()
メソッドを変更します。
renderButtons(m, task, index) {
m.onclick(e => this.updateForm.editTask(task));
m.span('Edit', { class: 'button' });
m.onclick(e => {
if (confirm('Are you sure you want to delete this task?'))
this.agent.destroyTask(task);
});
m.span('Delete', { class: 'button' });
if (index === 0) m.class('disabled');
else m.onclick(e => this.agent.patch('move_higher', task.id));
m.span({ class: 'button' }, m => m.fa('arrow-circle-up'));
if (index === this.agent.objects.length - 1) m.class('disabled');
else m.onclick(e => this.agent.patch('move_lower', task.id));
m.span({ class: 'button' }, m => m.fa('arrow-circle-down'));
}
変更したのは 2 行目です。this.editTask(task)
を this.updateForm.editTask(task)
に変更しました。
以上でいったん書き換え終了です。ブラウザで動作確認してください。
なお、書き換え後のソースコードは
https://github.com/oiax/capejs-tut2-app-source/archive/lecture-03.zip
から取得できます。
また、今回の変更内容は
- 51ec1d1 Extract UpdateForm partial component
で確認できます。
3 回に続いた「部分コンポーネント」に関するチュートリアルはこれで終了です。
次回からしばらくの間は、Cape.JS のルーティングについて書く予定です。お楽しみに。