今回はデータの編集と編集のキャンセル機能のあるフォームを作成する方法を解説します。
言葉だとわかりにくいので完成品から。
(自作のTodoアプリから抜粋)

入力後「保存」を押すと保存される。
はじめに試したこと
はじめに何も考えずにコードを書いてみました。
「フォームの入力内容をタスクに反映させたいからv-modelで結びつけて、submitされたときにタスクを編集するメソッド設定すれば行けるやろ〜」
taskをv-forでループさせて、isEditingでフォームの出し分け(編集モードへの切り替え)を行っています。
<tr v-for="(task, index) in allTasks" v-bind:key="task.id">
<td>
<div v-if="!isEditing[index]">
<span>{{ task.body }}</span>
</div>
<form v-if="isEditing[index]" @submit.prevent="editTask(index, task)">
<input type="text" v-model="task.body">
<div>
<button type="submit">保存</button>
<button type="button" @click="isEditing[index] = false">キャンセル</button>
</div>
</form>
<td>
</tr>
結論から言うと、これではうまくいきません...
なにがうまく行かないのか順を追って見ていきましょう。
まず大前提として、v-modelはv-bindとv-onをまとめて記述する方法に過ぎません。
なので、以下の2つのコードは同じ動きをします。
<input v-model="task.body"> <input v-bind:value="task.body" v-on:input="task.body = $event.target.value">
つまり、v-modelの内部ではv-bind:valueで値を結びつけて、v-on:inputで値を更新しています。
したがって上記のコードで「キャンセル」ボタンを押しても、inputに値が入った時点で更新されているのでその値が保存されてしまいます。
(つまり、formに設定してたEditTaskメソッドは意味をなしてなかった...)
今回のようなケースではinputに値を入力した時点では更新してほしくないため、改善していきましょう。

値を更新するタイミングをかえる
コードを以下のように修正します。
<tr v-for="(task, index) in allTasks" v-bind:key="task.id">
<td>
<div v-if="!isEditing[index]">
<span>{{ task.body }}</span>
</div>
<form v-if="isEditing[index]" @submit.prevent="editTask(index, task)">
<input type="text" :value="task.body" :ref="task.id"> //ここを変更
<div>
<button type="submit">保存</button>
<button type="button" @click="isEditing[index] = false">キャンセル</button>
</div>
</form>
<td>
</tr>
export default {
methods: {
async editTask(key, task) {
task.body = this.$refs[task.id][0].value
await this.$store.dispatch('task/edit', task)
this.$set(this.isEditing, key, false)
},
}
}
順番に解説していきます。
inputタグでは値のバインドだけを行う
まず今までv-modelにしていた部分は、:valueに変更し、編集モードにしたときにinputタグに入っている値をバインドするだけにしました。
つまり編集モードにして値を変更しても、このタイミングでは値の更新は行われません。
formタグに送信イベントを設定する
今回、値の更新はtype="submit"の「保存」ボタンを押されたときにだけ行います。
そのため、formタグに@submit.prevent="editTask"と設定した、editTask内で値の更新を行います。
async editTask(key, task) {
task.body = this.$refs[task.id][0].value
await this.$store.dispatch('task/edit', task)
this.$set(this.isEditing, key, false)
},
まず、editTaskに引数として渡したtaskのbodyにthis.$refs[task.id][0].valueとしてinputタグ内の値を代入します。
(inputタグに:ref="task.id"と動的にrefを設定しているので、配列形式で$refs[task.id][0].valueとすることで値を取得しています。)

その後Vuexのeditアクションを呼び出して値を更新します。
(Vuexを使わない場合はこのメソッド内でapiへのアクセスを行いましょう。)
最後に編集モードをオフにして終了です。
最後に
他のVue.js関連の記事はこちら!