LoginSignup
14
12

More than 5 years have passed since last update.

【Vue】v-forでリストレンダリングしたv-modelが連動してしまう

Last updated at Posted at 2018-07-28

なぜ?

入力フォームを追加&削除できるようにv-forでリストレンダリングさせて作っていたら、
1つの入力フィールドを変更すると、他の入力フィールドも連動して変更されてしまう・・・
v-modelでデータバインディングはそれぞれの要素を指しているのになんで???😭😭😭

ソースコード

以下のソースになります(サンプル)

  <div id="app">
    <div v-for="(item, index) in items"
         :key="index"
    >
      <div>
        <div>
          <h3>No.{{ index + 1 }}.</h3>
          <a v-if="totalCount > 1"
             href="#"
             @click.prevent="removeItems(index)"
          >削除</a>
        </div>

        <div>
          <p>タイトル</p>
          <input type="text" v-model="item.title">
        </div>

        <div>
          <p>内容</p>
          <textarea v-model="item.content"></textarea>
        </div>
      </div>
    </div>
    <a v-if="!isLimitOver" href="#" @click.prevent="addItems">追加する</a>
  </div>

const DEFAULT = {
    title: 'タイトル',
    content: '内容'
}

new Vue({
  el: '#app',
  data () {
    return {
      items: [DEFAULT], 
      count: 1,
      limit: 10
    }
  },
  computed: {
    isLimitOver () {
      return this.items.length >= this.limit
    },
    totalCount () {
      return this.items.length
    }
  },
  methods: {
    addItems () {
      this.items.push(DEFAULT)
      this.count++
    },
    removeItems (target) {
      this.items.splice(target, 1)
      this.count--
    }
  }
})

実際に動くfiddleはこちら

原因

↓デフォルトの値を定義したこの定数オブジェクトが原因でした😂

const DEFAULT = {
    title: 'タイトル',
    content: '内容'
}

これは追加するたびに、定義された同じオブジェクトを配列にプッシュしていました。
したがって、新しい独立したオブジェクトではなく、同じオブジェクトの複数のコピーがある状態になっていたわけです💦
つまり、同じデータオブジェクトのため、全て共有されているので連動していたわけですね😓

解決策

データオブジェクトをコンポーネント内の関数にする✌️


new Vue({
  el: '#app',
  data () {
    return {
      items: [{
        title: 'タイトル',
        content: '内容'
      }], 
      count: 1,
      limit: 10
    }
  },
  computed: {
    isLimitOver () {
      return this.items.length >= this.limit
    },
    totalCount () {
      return this.items.length
    }
  },
  methods: {
    addItems () {
      this.items.push(this.independentObejct())
      this.count++
    },
    removeItems (target) {
      this.items.splice(target, 1)
      this.count--
    },
     /**
     * 追加した関数
     * @return {[Object]}
     */
    independentObejct () {
      return {
        title: 'タイトル',
        content: '内容'
      }
    }
  }
})

コンポーネントのVueインスタンスが同じデータオブジェクトを共有しないように、
independentObejct()メソッドとしてデータオブジェクトを関数にしました

修正後はこちら

参考

14
12
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
12