Browse Source

fix(v-model): use stricter check for <select> option update

close #6112
Evan You 9 years ago
parent
commit
c70addf7d1

+ 4 - 13
src/platforms/web/runtime/directives/model.js

@@ -30,6 +30,7 @@ export default {
       if (isIE || isEdge) {
         setTimeout(cb, 0)
       }
+      el._vOptions = [].map.call(el.options, getValue)
     } else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
       el._vModifiers = binding.modifiers
       if (!binding.modifiers.lazy) {
@@ -56,10 +57,9 @@ export default {
       // it's possible that the value is out-of-sync with the rendered options.
       // detect such cases and filter out values that no longer has a matching
       // option in the DOM.
-      const needReset = el.multiple
-        ? binding.value.some(v => hasNoMatchingOption(v, el.options))
-        : binding.value !== binding.oldValue && hasNoMatchingOption(binding.value, el.options)
-      if (needReset) {
+      const prevOptions = el._vOptions
+      const curOptions = el._vOptions = [].map.call(el.options, getValue)
+      if (curOptions.some((o, i) => !looseEqual(o, prevOptions[i]))) {
         trigger(el, 'change')
       }
     }
@@ -101,15 +101,6 @@ function setSelected (el, binding, vm) {
   }
 }
 
-function hasNoMatchingOption (value, options) {
-  for (let i = 0, l = options.length; i < l; i++) {
-    if (looseEqual(getValue(options[i]), value)) {
-      return false
-    }
-  }
-  return true
-}
-
 function getValue (option) {
   return '_value' in option
     ? option._value

+ 18 - 0
test/unit/features/directives/model-select.spec.js

@@ -471,4 +471,22 @@ describe('Directive v-model select', () => {
       expect(vm.$el.childNodes[0].selected).toBe(true)
     }).then(done)
   })
+
+  // #6112
+  it('should not set non-matching value to undefined if options did not change', done => {
+    const vm = new Vue({
+      data: {
+        test: '1'
+      },
+      template:
+        '<select v-model="test">' +
+          '<option>a</option>' +
+        '</select>'
+    }).$mount()
+
+    vm.test = '2'
+    waitForUpdate(() => {
+      expect(vm.test).toBe('2')
+    }).then(done)
+  })
 })