Browse Source

fix(model): correctly set select v-model initial value on patch (#6910)

laoxiong 8 years ago
parent
commit
58a39dfa0e

+ 20 - 3
src/platforms/web/runtime/directives/model.js

@@ -6,6 +6,8 @@
 import { isTextInputType } from 'web/util/element'
 import { looseEqual, looseIndexOf } from 'shared/util'
 import { warn, isAndroid, isIE9, isIE, isEdge } from 'core/util/index'
+import { mergeVNodeHook } from 'core/vdom/helpers/index'
+import { emptyNode } from 'core/vdom/patch'
 
 /* istanbul ignore if */
 if (isIE9) {
@@ -18,10 +20,17 @@ if (isIE9) {
   })
 }
 
-export default {
-  inserted (el, binding, vnode) {
+const directive = {
+  inserted (el, binding, vnode, oldVnode) {
     if (vnode.tag === 'select') {
-      setSelected(el, binding, vnode.context)
+      // #6903
+      if (oldVnode !== emptyNode && !hasDirective(oldVnode, 'model')) {
+        mergeVNodeHook(vnode.data.hook || (vnode.data.hook = {}), 'postpatch', () => {
+          directive.componentUpdated(el, binding, vnode)
+        })
+      } else {
+        setSelected(el, binding, vnode.context)
+      }
       el._vOptions = [].map.call(el.options, getValue)
     } else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
       el._vModifiers = binding.modifiers
@@ -136,3 +145,11 @@ function trigger (el, type) {
   e.initEvent(type, true, true)
   el.dispatchEvent(e)
 }
+
+function hasDirective (vnode, dirname) {
+  return vnode.data &&
+    vnode.data.directives &&
+    vnode.data.directives.some(dir => dir.name === dirname)
+}
+
+export default directive

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

@@ -546,4 +546,46 @@ describe('Directive v-model select', () => {
       expect(spy).not.toHaveBeenCalled()
     }).then(done)
   })
+
+  // #6903
+  describe('should correctly handle v-model when the vnodes are the same', () => {
+    function makeInstance (foo) {
+      return new Vue({
+        data: {
+          foo: foo,
+          options: ['b', 'c', 'd'],
+          value: 'c'
+        },
+        template:
+          '<div>' +
+            '<select v-if="foo" data-attr>' +
+              '<option selected>a</option>' +
+            '</select>' +
+            '<select v-else v-model="value">' +
+              '<option v-for="option in options" :value="option">{{ option }}</option>' +
+            '</select>' +
+          '</div>'
+      }).$mount()
+    }
+
+    it('register v-model', done => {
+      const vm = makeInstance(true)
+
+      expect(vm.$el.firstChild.selectedIndex).toBe(0)
+      vm.foo = false
+      waitForUpdate(() => {
+        expect(vm.$el.firstChild.selectedIndex).toBe(1)
+      }).then(done)
+    })
+
+    it('remove v-model', done => {
+      const vm = makeInstance(false)
+
+      expect(vm.$el.firstChild.selectedIndex).toBe(1)
+      vm.foo = true
+      waitForUpdate(() => {
+        expect(vm.$el.firstChild.selectedIndex).toBe(0)
+      }).then(done)
+    })
+  })
 })