Web制作・プログラミング関連のおすすめレビュー記事はこちら

【Vue.js】編集機能とキャンセル機能のあるフォームを実装

Vue.js

今回はデータの編集と編集のキャンセル機能のあるフォームを作成する方法を解説します。

言葉だとわかりにくいので完成品から。

(自作の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に値を入力した時点では更新してほしくないため、改善していきましょう。

参考記事
Vue.jsのv-modelを正しく使う - Qiita
はじめに v-modelはVue.jsを使ってフォームを構築する際によく使う機能です。 しかし、意外ときちんと使えていない場面を見かけることが多いので仕様と間違えやすいところを簡単に整理します。 v-modelの動作 公式...

値を更新するタイミングをかえる

コードを以下のように修正します。

HTML部分
<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>
JavaScript部分
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とすることで値を取得しています。)

動的なrefsに関しての参考記事はこちら
Vue.jsで動的にrefにアクセスする方法 - Qiita
refに変数を使いたい templateにはv-bindディレクティブを使ってv-bind:refや:refと書けばいいことは分かるけど、それに対してscriptでどうアクセスするか分からなくて少しスタックしたのでメモ。 公式...

その後Vuexのeditアクションを呼び出して値を更新します。

(Vuexを使わない場合はこのメソッド内でapiへのアクセスを行いましょう。)

最後に編集モードをオフにして終了です。

最後に

他のVue.js関連の記事はこちら!

タイトルとURLをコピーしました