From 6f9d43815ca3103c697b98d4aa5ee6eec96ec1ca Mon Sep 17 00:00:00 2001 From: defcc Date: Tue, 3 Jan 2017 23:46:42 +0800 Subject: [PATCH 1/2] listen to click event for checkbox and radio. --- src/platforms/web/compiler/directives/model.js | 4 ++-- src/platforms/web/runtime/modules/dom-props.js | 16 ++++------------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/platforms/web/compiler/directives/model.js b/src/platforms/web/compiler/directives/model.js index e55d1ad009e..a86f7d7eda3 100644 --- a/src/platforms/web/compiler/directives/model.js +++ b/src/platforms/web/compiler/directives/model.js @@ -59,7 +59,7 @@ function genCheckboxModel ( `?_i(${value},${valueBinding})>-1` + `:_q(${value},${trueValueBinding})` ) - addHandler(el, 'change', + addHandler(el, 'click', `var $$a=${value},` + '$$el=$event.target,' + `$$c=$$el.checked?(${trueValueBinding}):(${falseValueBinding});` + @@ -90,7 +90,7 @@ function genRadioModel ( let valueBinding = getBindingAttr(el, 'value') || 'null' valueBinding = number ? `_n(${valueBinding})` : valueBinding addProp(el, 'checked', `_q(${value},${valueBinding})`) - addHandler(el, 'change', genAssignmentCode(value, valueBinding), null, true) + addHandler(el, 'click', genAssignmentCode(value, valueBinding), null, true) } function genDefaultModel ( diff --git a/src/platforms/web/runtime/modules/dom-props.js b/src/platforms/web/runtime/modules/dom-props.js index 1877cc7bd43..d2b142f1385 100644 --- a/src/platforms/web/runtime/modules/dom-props.js +++ b/src/platforms/web/runtime/modules/dom-props.js @@ -29,13 +29,7 @@ function updateDOMProps (oldVnode: VNodeWithData, vnode: VNodeWithData) { if (vnode.children) vnode.children.length = 0 if (cur === oldProps[key]) continue } - // #4521: if a click event triggers update before the change event is - // dispatched on a checkbox/radio input, the input's checked state will - // be reset and fail to trigger another update. - /* istanbul ignore next */ - if (key === 'checked' && !isDirty(elm, cur)) { - continue - } + if (key === 'value') { // store value as _value as well since // non-string values will be stringified @@ -59,17 +53,15 @@ function shouldUpdateValue ( vnode: VNodeWithData, checkVal: string ): boolean { - if (!elm.composing && ( + return (!elm.composing && ( vnode.tag === 'option' || isDirty(elm, checkVal) || isInputChanged(vnode, checkVal) - )) { - return true - } - return false + )) } function isDirty (elm: acceptValueElm, checkVal: string): boolean { + // return true when textbox (.number and .trim) loses focus and its value is not equal to the updated value return document.activeElement !== elm && elm.value !== checkVal } From a8fb641989942df8a261a213e9bbf1f136722ce6 Mon Sep 17 00:00:00 2001 From: defcc Date: Wed, 4 Jan 2017 01:21:07 +0800 Subject: [PATCH 2/2] add test cases --- .../directives/model-checkbox.spec.js | 44 ++++++++++++++- .../features/directives/model-radio.spec.js | 55 +++++++++++++++++++ 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/test/unit/features/directives/model-checkbox.spec.js b/test/unit/features/directives/model-checkbox.spec.js index 9f061d40052..1e6f68b04d0 100644 --- a/test/unit/features/directives/model-checkbox.spec.js +++ b/test/unit/features/directives/model-checkbox.spec.js @@ -148,7 +148,7 @@ describe('Directive v-model checkbox', () => { ` }).$mount() document.body.appendChild(vm.$el) - var checkboxInputs = vm.$el.getElementsByTagName('input') + const checkboxInputs = vm.$el.getElementsByTagName('input') expect(checkboxInputs[0].checked).toBe(false) expect(checkboxInputs[1].checked).toBe(false) expect(checkboxInputs[2].checked).toBe(true) @@ -173,7 +173,7 @@ describe('Directive v-model checkbox', () => { '' + '' }).$mount() - var checkboxInput = vm.$el.children + const checkboxInput = vm.$el.children expect(checkboxInput[0].checked).toBe(false) expect(checkboxInput[1].checked).toBe(true) expect(checkboxInput[2].checked).toBe(false) @@ -217,6 +217,46 @@ describe('Directive v-model checkbox', () => { }).then(done) }) + // #4521 + it('should work with click event', (done) => { + const vm = new Vue({ + data: { + num: 1, + checked: false + }, + template: '
click {{ num }}
', + methods: { + add: function () { + this.num++ + } + } + }).$mount() + document.body.appendChild(vm.$el) + const checkbox = vm.$refs.checkbox + checkbox.click() + waitForUpdate(() => { + expect(checkbox.checked).toBe(true) + expect(vm.num).toBe(2) + }).then(done) + }) + + it('should get updated with model when in focus', (done) => { + const vm = new Vue({ + data: { + a: '2' + }, + template: '' + }).$mount() + document.body.appendChild(vm.$el) + vm.$el.click() + waitForUpdate(() => { + expect(vm.$el.checked).toBe(true) + vm.a = 2 + }).then(() => { + expect(vm.$el.checked).toBe(false) + }).then(done) + }) + it('warn inline checked', () => { const vm = new Vue({ template: ``, diff --git a/test/unit/features/directives/model-radio.spec.js b/test/unit/features/directives/model-radio.spec.js index 25e8752f623..4d46f22d3d1 100644 --- a/test/unit/features/directives/model-radio.spec.js +++ b/test/unit/features/directives/model-radio.spec.js @@ -197,6 +197,61 @@ describe('Directive v-model radio', () => { }).then(done) }) + // #4521 + it('should work with click event', (done) => { + const vm = new Vue({ + data: { + num: 1, + checked: 1 + }, + template: + '
' + + 'click {{ num }}' + + '' + + '
', + methods: { + add: function () { + this.num++ + } + } + }).$mount() + document.body.appendChild(vm.$el) + const radios = vm.$el.getElementsByTagName('input') + radios[0].click() + waitForUpdate(() => { + expect(radios[0].checked).toBe(true) + expect(radios[1].checked).toBe(false) + expect(vm.num).toBe(2) + radios[0].click() + }).then(() => { + expect(radios[0].checked).toBe(true) + expect(radios[1].checked).toBe(false) + expect(vm.num).toBe(3) + radios[1].click() + }).then(() => { + expect(radios[0].checked).toBe(false) + expect(radios[1].checked).toBe(true) + expect(vm.num).toBe(4) + }).then(done) + }) + + it('should get updated with model when in focus', (done) => { + const vm = new Vue({ + data: { + a: '2' + }, + template: '' + }).$mount() + document.body.appendChild(vm.$el) + vm.$el.click() + waitForUpdate(() => { + expect(vm.$el.checked).toBe(true) + vm.a = 2 + }).then(() => { + expect(vm.$el.checked).toBe(false) + }).then(done) + }) + it('warn inline checked', () => { const vm = new Vue({ template: ``,