Skip to content

Commit da955d1

Browse files
defccyyx990803
authored andcommitted
Use 'click' event for checkbox and radio (fix #4620) (#4639)
* listen to click event for checkbox and radio. * add test cases
1 parent d3768c0 commit da955d1

File tree

4 files changed

+103
-16
lines changed

4 files changed

+103
-16
lines changed

src/platforms/web/compiler/directives/model.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ function genCheckboxModel (
6262
: `:_q(${value},${trueValueBinding})`
6363
)
6464
)
65-
addHandler(el, 'change',
65+
addHandler(el, 'click',
6666
`var $$a=${value},` +
6767
'$$el=$event.target,' +
6868
`$$c=$$el.checked?(${trueValueBinding}):(${falseValueBinding});` +
@@ -93,7 +93,7 @@ function genRadioModel (
9393
let valueBinding = getBindingAttr(el, 'value') || 'null'
9494
valueBinding = number ? `_n(${valueBinding})` : valueBinding
9595
addProp(el, 'checked', `_q(${value},${valueBinding})`)
96-
addHandler(el, 'change', genAssignmentCode(value, valueBinding), null, true)
96+
addHandler(el, 'click', genAssignmentCode(value, valueBinding), null, true)
9797
}
9898

9999
function genDefaultModel (

src/platforms/web/runtime/modules/dom-props.js

+4-12
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,7 @@ function updateDOMProps (oldVnode: VNodeWithData, vnode: VNodeWithData) {
2929
if (vnode.children) vnode.children.length = 0
3030
if (cur === oldProps[key]) continue
3131
}
32-
// #4521: if a click event triggers update before the change event is
33-
// dispatched on a checkbox/radio input, the input's checked state will
34-
// be reset and fail to trigger another update.
35-
/* istanbul ignore next */
36-
if (key === 'checked' && !isDirty(elm, cur)) {
37-
continue
38-
}
32+
3933
if (key === 'value') {
4034
// store value as _value as well since
4135
// non-string values will be stringified
@@ -59,17 +53,15 @@ function shouldUpdateValue (
5953
vnode: VNodeWithData,
6054
checkVal: string
6155
): boolean {
62-
if (!elm.composing && (
56+
return (!elm.composing && (
6357
vnode.tag === 'option' ||
6458
isDirty(elm, checkVal) ||
6559
isInputChanged(vnode, checkVal)
66-
)) {
67-
return true
68-
}
69-
return false
60+
))
7061
}
7162

7263
function isDirty (elm: acceptValueElm, checkVal: string): boolean {
64+
// return true when textbox (.number and .trim) loses focus and its value is not equal to the updated value
7365
return document.activeElement !== elm && elm.value !== checkVal
7466
}
7567

test/unit/features/directives/model-checkbox.spec.js

+42-2
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ describe('Directive v-model checkbox', () => {
148148
`
149149
}).$mount()
150150
document.body.appendChild(vm.$el)
151-
var checkboxInputs = vm.$el.getElementsByTagName('input')
151+
const checkboxInputs = vm.$el.getElementsByTagName('input')
152152
expect(checkboxInputs[0].checked).toBe(false)
153153
expect(checkboxInputs[1].checked).toBe(false)
154154
expect(checkboxInputs[2].checked).toBe(true)
@@ -173,7 +173,7 @@ describe('Directive v-model checkbox', () => {
173173
'<input type="checkbox" value="true" v-model="test">' +
174174
'</div>'
175175
}).$mount()
176-
var checkboxInput = vm.$el.children
176+
const checkboxInput = vm.$el.children
177177
expect(checkboxInput[0].checked).toBe(false)
178178
expect(checkboxInput[1].checked).toBe(true)
179179
expect(checkboxInput[2].checked).toBe(false)
@@ -217,6 +217,46 @@ describe('Directive v-model checkbox', () => {
217217
}).then(done)
218218
})
219219

220+
// #4521
221+
it('should work with click event', (done) => {
222+
const vm = new Vue({
223+
data: {
224+
num: 1,
225+
checked: false
226+
},
227+
template: '<div @click="add">click {{ num }}<input ref="checkbox" type="checkbox" v-model="checked"/></div>',
228+
methods: {
229+
add: function () {
230+
this.num++
231+
}
232+
}
233+
}).$mount()
234+
document.body.appendChild(vm.$el)
235+
const checkbox = vm.$refs.checkbox
236+
checkbox.click()
237+
waitForUpdate(() => {
238+
expect(checkbox.checked).toBe(true)
239+
expect(vm.num).toBe(2)
240+
}).then(done)
241+
})
242+
243+
it('should get updated with model when in focus', (done) => {
244+
const vm = new Vue({
245+
data: {
246+
a: '2'
247+
},
248+
template: '<input type="checkbox" value="1" v-model="a"/>'
249+
}).$mount()
250+
document.body.appendChild(vm.$el)
251+
vm.$el.click()
252+
waitForUpdate(() => {
253+
expect(vm.$el.checked).toBe(true)
254+
vm.a = 2
255+
}).then(() => {
256+
expect(vm.$el.checked).toBe(false)
257+
}).then(done)
258+
})
259+
220260
it('warn inline checked', () => {
221261
const vm = new Vue({
222262
template: `<input type="checkbox" v-model="test" checked>`,

test/unit/features/directives/model-radio.spec.js

+55
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,61 @@ describe('Directive v-model radio', () => {
197197
}).then(done)
198198
})
199199

200+
// #4521
201+
it('should work with click event', (done) => {
202+
const vm = new Vue({
203+
data: {
204+
num: 1,
205+
checked: 1
206+
},
207+
template:
208+
'<div @click="add">' +
209+
'click {{ num }}<input name="test" type="radio" value="1" v-model="checked"/>' +
210+
'<input name="test" type="radio" value="2" v-model="checked"/>' +
211+
'</div>',
212+
methods: {
213+
add: function () {
214+
this.num++
215+
}
216+
}
217+
}).$mount()
218+
document.body.appendChild(vm.$el)
219+
const radios = vm.$el.getElementsByTagName('input')
220+
radios[0].click()
221+
waitForUpdate(() => {
222+
expect(radios[0].checked).toBe(true)
223+
expect(radios[1].checked).toBe(false)
224+
expect(vm.num).toBe(2)
225+
radios[0].click()
226+
}).then(() => {
227+
expect(radios[0].checked).toBe(true)
228+
expect(radios[1].checked).toBe(false)
229+
expect(vm.num).toBe(3)
230+
radios[1].click()
231+
}).then(() => {
232+
expect(radios[0].checked).toBe(false)
233+
expect(radios[1].checked).toBe(true)
234+
expect(vm.num).toBe(4)
235+
}).then(done)
236+
})
237+
238+
it('should get updated with model when in focus', (done) => {
239+
const vm = new Vue({
240+
data: {
241+
a: '2'
242+
},
243+
template: '<input type="radio" value="1" v-model="a"/>'
244+
}).$mount()
245+
document.body.appendChild(vm.$el)
246+
vm.$el.click()
247+
waitForUpdate(() => {
248+
expect(vm.$el.checked).toBe(true)
249+
vm.a = 2
250+
}).then(() => {
251+
expect(vm.$el.checked).toBe(false)
252+
}).then(done)
253+
})
254+
200255
it('warn inline checked', () => {
201256
const vm = new Vue({
202257
template: `<input v-model="test" type="radio" value="1" checked>`,

0 commit comments

Comments
 (0)