From bbd13afa0930cbc5b99e11d460c599a54c4ccc0f Mon Sep 17 00:00:00 2001 From: Chris Casola Date: Tue, 3 Oct 2017 17:48:56 -0400 Subject: [PATCH] fix(v-model): update model correctly when input event is bound Use the "input" event instead of "change" when listening for changes to the selection state since the "input" event will always fire before the "change" event. This avoids an issue where a user binds to the "input" event with "v-bind" and causes the component to update and set its model value back to the value of the select before it has received the new selection value. Fixes #6690 --- .../web/compiler/directives/model.js | 2 +- src/platforms/web/runtime/directives/model.js | 4 +- .../features/directives/model-select.spec.js | 57 ++++++++++++++----- 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/platforms/web/compiler/directives/model.js b/src/platforms/web/compiler/directives/model.js index 7c662629b65..4612fdfc16e 100644 --- a/src/platforms/web/compiler/directives/model.js +++ b/src/platforms/web/compiler/directives/model.js @@ -126,7 +126,7 @@ function genSelect ( const assignment = '$event.target.multiple ? $$selectedVal : $$selectedVal[0]' let code = `var $$selectedVal = ${selectedVal};` code = `${code} ${genAssignmentCode(value, assignment)}` - addHandler(el, 'change', code, null, true) + addHandler(el, 'input', code, null, true) } function genDefaultModel ( diff --git a/src/platforms/web/runtime/directives/model.js b/src/platforms/web/runtime/directives/model.js index 89239816882..98f53e96a42 100644 --- a/src/platforms/web/runtime/directives/model.js +++ b/src/platforms/web/runtime/directives/model.js @@ -52,13 +52,13 @@ export default { const prevOptions = el._vOptions const curOptions = el._vOptions = [].map.call(el.options, getValue) if (curOptions.some((o, i) => !looseEqual(o, prevOptions[i]))) { - // trigger change event if + // trigger input event if // no matching option found for at least one value const needReset = el.multiple ? binding.value.some(v => hasNoMatchingOption(v, curOptions)) : binding.value !== binding.oldValue && hasNoMatchingOption(binding.value, curOptions) if (needReset) { - trigger(el, 'change') + trigger(el, 'input') } } } diff --git a/test/unit/features/directives/model-select.spec.js b/test/unit/features/directives/model-select.spec.js index 7c265d25f79..e9a501f8cbe 100644 --- a/test/unit/features/directives/model-select.spec.js +++ b/test/unit/features/directives/model-select.spec.js @@ -56,7 +56,7 @@ describe('Directive v-model select', () => { expect(vm.$el.value).toBe('c') expect(vm.$el.childNodes[2].selected).toBe(true) updateSelect(vm.$el, 'a') - triggerEvent(vm.$el, 'change') + triggerEvent(vm.$el, 'input') expect(vm.test).toBe('a') }).then(done) }) @@ -82,11 +82,11 @@ describe('Directive v-model select', () => { expect(vm.$el.childNodes[2].selected).toBe(true) updateSelect(vm.$el, '1') - triggerEvent(vm.$el, 'change') + triggerEvent(vm.$el, 'input') expect(vm.test).toBe('1') updateSelect(vm.$el, '2') - triggerEvent(vm.$el, 'change') + triggerEvent(vm.$el, 'input') expect(vm.test).toBe(2) }).then(done) }) @@ -110,11 +110,11 @@ describe('Directive v-model select', () => { expect(vm.$el.childNodes[2].selected).toBe(true) updateSelect(vm.$el, '1') - triggerEvent(vm.$el, 'change') + triggerEvent(vm.$el, 'input') expect(vm.test).toBe('1') updateSelect(vm.$el, { a: 2 }) - triggerEvent(vm.$el, 'change') + triggerEvent(vm.$el, 'input') expect(vm.test).toEqual({ a: 2 }) }).then(done) }) @@ -138,11 +138,11 @@ describe('Directive v-model select', () => { expect(vm.$el.childNodes[2].selected).toBe(true) updateSelect(vm.$el, '1') - triggerEvent(vm.$el, 'change') + triggerEvent(vm.$el, 'input') expect(vm.test).toBe('1') updateSelect(vm.$el, [{ a: 2 }]) - triggerEvent(vm.$el, 'change') + triggerEvent(vm.$el, 'input') expect(vm.test).toEqual([{ a: 2 }]) }).then(done) }) @@ -167,7 +167,7 @@ describe('Directive v-model select', () => { expect(vm.$el.value).toBe('c') expect(vm.$el.childNodes[2].selected).toBe(true) updateSelect(vm.$el, 'a') - triggerEvent(vm.$el, 'change') + triggerEvent(vm.$el, 'input') expect(vm.test).toBe('a') // update v-for opts vm.opts = ['d', 'a'] @@ -196,7 +196,7 @@ describe('Directive v-model select', () => { expect(vm.$el.value).toBe('3') expect(vm.$el.childNodes[2].selected).toBe(true) updateSelect(vm.$el, 1) - triggerEvent(vm.$el, 'change') + triggerEvent(vm.$el, 'input') expect(vm.test).toBe(1) // update v-for opts vm.opts = [0, 1] @@ -256,7 +256,7 @@ describe('Directive v-model select', () => { expect(opts[2].selected).toBe(true) opts[0].selected = false opts[1].selected = true - triggerEvent(vm.$el, 'change') + triggerEvent(vm.$el, 'input') expect(vm.test).toEqual(['b', 'c']) }).then(done) }) @@ -283,14 +283,14 @@ describe('Directive v-model select', () => { expect(opts[2].selected).toBe(true) opts[0].selected = false opts[1].selected = true - triggerEvent(vm.$el, 'change') + triggerEvent(vm.$el, 'input') expect(vm.test).toEqual(['b', 'c']) // update v-for opts vm.opts = ['c', 'd'] }).then(() => { expect(opts[0].selected).toBe(true) expect(opts[1].selected).toBe(false) - expect(vm.test).toEqual(['c']) // should remove 'd' which no longer has a matching option + expect(vm.test).toEqual(['c']) // should remove 'b' which no longer has a matching option }).then(done) }) } @@ -313,7 +313,7 @@ describe('Directive v-model select', () => { }).$mount() document.body.appendChild(vm.$el) vm.$el.options[1].selected = true - triggerEvent(vm.$el, 'change') + triggerEvent(vm.$el, 'input') waitForUpdate(() => { expect(spy).toHaveBeenCalled() expect(vm.selections).toEqual(['1', '2']) @@ -382,7 +382,7 @@ describe('Directive v-model select', () => { var selects = vm.$el.getElementsByTagName('select') var select0 = selects[0] select0.options[0].selected = true - triggerEvent(select0, 'change') + triggerEvent(select0, 'input') waitForUpdate(() => { expect(spy).toHaveBeenCalled() expect(vm.selections).toEqual(['foo', '']) @@ -403,7 +403,7 @@ describe('Directive v-model select', () => { }).$mount() document.body.appendChild(vm.$el) updateSelect(vm.$el, '1') - triggerEvent(vm.$el, 'change') + triggerEvent(vm.$el, 'input') expect(vm.test).toBe(1) }) @@ -546,4 +546,31 @@ describe('Directive v-model select', () => { expect(spy).not.toHaveBeenCalled() }).then(done) }) + + // #6690 + it('should work on an element with an input binding', done => { + const vm = new Vue({ + data: { + test1: '', + inputEvaluated: '' + }, + template: + `' + }).$mount() + document.body.appendChild(vm.$el) + + vm.$el.children[1].selected = true + + triggerEvent(vm.$el, 'input') + + waitForUpdate(() => { + triggerEvent(vm.$el, 'change') + }).then(() => { + expect(vm.$el.value).toBe('alpha') + }).then(done) + }) })