feat(vModel): handle true-value and false-value for checkbox (#449)

This commit is contained in:
Cédric Exbrayat 2019-11-12 22:24:39 +01:00 committed by Evan You
parent 0ef999bef1
commit fe66194a77
3 changed files with 117 additions and 4 deletions

View File

@ -189,6 +189,101 @@ describe('vModel', () => {
data.value = false data.value = false
await nextTick() await nextTick()
expect(input.checked).toEqual(false) expect(input.checked).toEqual(false)
data.value = true
await nextTick()
expect(input.checked).toEqual(true)
input.checked = false
triggerEvent('change', input)
await nextTick()
expect(data.value).toEqual(false)
})
it('should work with checkbox and true-value/false-value', async () => {
const component = createComponent({
data() {
return { value: null }
},
render() {
return [
withVModel(
h('input', {
type: 'checkbox',
'true-value': 'yes',
'false-value': 'no',
'onUpdate:modelValue': setValue.bind(this)
}),
this.value
)
]
}
})
app.mount(component, root)
const input = root.querySelector('input')
const data = root._vnode.component.data
input.checked = true
triggerEvent('change', input)
await nextTick()
expect(data.value).toEqual('yes')
data.value = 'no'
await nextTick()
expect(input.checked).toEqual(false)
data.value = 'yes'
await nextTick()
expect(input.checked).toEqual(true)
input.checked = false
triggerEvent('change', input)
await nextTick()
expect(data.value).toEqual('no')
})
it('should work with checkbox and true-value/false-value with object values', async () => {
const component = createComponent({
data() {
return { value: null }
},
render() {
return [
withVModel(
h('input', {
type: 'checkbox',
'true-value': { yes: 'yes' },
'false-value': { no: 'no' },
'onUpdate:modelValue': setValue.bind(this)
}),
this.value
)
]
}
})
app.mount(component, root)
const input = root.querySelector('input')
const data = root._vnode.component.data
input.checked = true
triggerEvent('change', input)
await nextTick()
expect(data.value).toEqual({ yes: 'yes' })
data.value = { no: 'no' }
await nextTick()
expect(input.checked).toEqual(false)
data.value = { yes: 'yes' }
await nextTick()
expect(input.checked).toEqual(true)
input.checked = false
triggerEvent('change', input)
await nextTick()
expect(data.value).toEqual({ no: 'no' })
}) })
it(`should support array as a checkbox model`, async () => { it(`should support array as a checkbox model`, async () => {

View File

@ -101,7 +101,7 @@ export const vModelCheckbox: ObjectDirective<HTMLInputElement> = {
assign(filtered) assign(filtered)
} }
} else { } else {
assign(checked) assign(getCheckboxValue(el, checked))
} }
}) })
}, },
@ -119,7 +119,7 @@ function setChecked(
if (isArray(value)) { if (isArray(value)) {
el.checked = looseIndexOf(value, vnode.props!.value) > -1 el.checked = looseIndexOf(value, vnode.props!.value) > -1
} else if (value !== oldValue) { } else if (value !== oldValue) {
el.checked = !!value el.checked = looseEqual(value, getCheckboxValue(el, true))
} }
} }
@ -228,6 +228,15 @@ function getValue(el: HTMLOptionElement | HTMLInputElement) {
return '_value' in el ? (el as any)._value : el.value return '_value' in el ? (el as any)._value : el.value
} }
// retrieve raw value for true-value and false-value set via :true-value or :false-value bindings
function getCheckboxValue(
el: HTMLInputElement & { _trueValue?: any; _falseValue?: any },
checked: boolean
) {
const key = checked ? '_trueValue' : '_falseValue'
return key in el ? el[key] : checked
}
export const vModelDynamic: ObjectDirective< export const vModelDynamic: ObjectDirective<
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
> = { > = {

View File

@ -5,9 +5,9 @@ import { patchDOMProp } from './modules/props'
import { patchEvent } from './modules/events' import { patchEvent } from './modules/events'
import { isOn } from '@vue/shared' import { isOn } from '@vue/shared'
import { import {
VNode,
ComponentInternalInstance, ComponentInternalInstance,
SuspenseBoundary SuspenseBoundary,
VNode
} from '@vue/runtime-core' } from '@vue/runtime-core'
export function patchProp( export function patchProp(
@ -53,6 +53,15 @@ export function patchProp(
unmountChildren unmountChildren
) )
} else { } else {
// special case for <input v-model type="checkbox"> with
// :true-value & :false-value
// store value as dom properties since non-string values will be
// stringified.
if (key === 'true-value') {
;(el as any)._trueValue = nextValue
} else if (key === 'false-value') {
;(el as any)._falseValue = nextValue
}
patchAttr(el, key, nextValue) patchAttr(el, key, nextValue)
} }
break break