feat(vModel): handle true-value and false-value for checkbox (#449)
This commit is contained in:
parent
0ef999bef1
commit
fe66194a77
@ -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 () => {
|
||||||
|
@ -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
|
||||||
> = {
|
> = {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user