data.value = false
await nextTick()
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 () => {
assign(filtered)
}
} else {
- assign(checked)
+ assign(getCheckboxValue(el, checked))
}
})
},
if (isArray(value)) {
el.checked = looseIndexOf(value, vnode.props!.value) > -1
} else if (value !== oldValue) {
- el.checked = !!value
+ el.checked = looseEqual(value, getCheckboxValue(el, true))
}
}
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<
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
> = {
import { patchEvent } from './modules/events'
import { isOn } from '@vue/shared'
import {
- VNode,
ComponentInternalInstance,
- SuspenseBoundary
+ SuspenseBoundary,
+ VNode
} from '@vue/runtime-core'
export function patchProp(
unmountChildren
)
} 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)
}
break