expect(bar.checked).toEqual(false)
})
+ it(`should support Set as a checkbox model`, async () => {
+ const component = defineComponent({
+ data() {
+ return { value: new Set() }
+ },
+ render() {
+ return [
+ withVModel(
+ h('input', {
+ type: 'checkbox',
+ class: 'foo',
+ value: 'foo',
+ 'onUpdate:modelValue': setValue.bind(this)
+ }),
+ this.value
+ ),
+ withVModel(
+ h('input', {
+ type: 'checkbox',
+ class: 'bar',
+ value: 'bar',
+ 'onUpdate:modelValue': setValue.bind(this)
+ }),
+ this.value
+ )
+ ]
+ }
+ })
+ render(h(component), root)
+
+ const foo = root.querySelector('.foo')
+ const bar = root.querySelector('.bar')
+ const data = root._vnode.component.data
+
+ foo.checked = true
+ triggerEvent('change', foo)
+ await nextTick()
+ expect(data.value).toMatchObject(new Set(['foo']))
+
+ bar.checked = true
+ triggerEvent('change', bar)
+ await nextTick()
+ expect(data.value).toMatchObject(new Set(['foo', 'bar']))
+
+ bar.checked = false
+ triggerEvent('change', bar)
+ await nextTick()
+ expect(data.value).toMatchObject(new Set(['foo']))
+
+ foo.checked = false
+ triggerEvent('change', foo)
+ await nextTick()
+ expect(data.value).toMatchObject(new Set())
+
+ data.value = new Set(['foo'])
+ await nextTick()
+ expect(bar.checked).toEqual(false)
+ expect(foo.checked).toEqual(true)
+
+ data.value = new Set(['bar'])
+ await nextTick()
+ expect(foo.checked).toEqual(false)
+ expect(bar.checked).toEqual(true)
+
+ data.value = new Set()
+ await nextTick()
+ expect(foo.checked).toEqual(false)
+ expect(bar.checked).toEqual(false)
+ })
+
it('should work with radio', async () => {
const component = defineComponent({
data() {
looseEqual,
looseIndexOf,
invokeArrayFns,
- toNumber
+ toNumber,
+ isSet,
+ looseHas
} from '@vue/shared'
type AssignerFn = (value: any) => void
filtered.splice(index, 1)
assign(filtered)
}
+ } else if (isSet(modelValue)) {
+ const found = modelValue.has(elementValue)
+ if (checked && !found) {
+ assign(modelValue.add(elementValue))
+ } else if (!checked && found) {
+ modelValue.delete(elementValue)
+ assign(modelValue)
+ }
} else {
assign(getCheckboxValue(el, checked))
}
;(el as any)._modelValue = value
if (isArray(value)) {
el.checked = looseIndexOf(value, vnode.props!.value) > -1
+ } else if (isSet(value)) {
+ el.checked = looseHas(value, vnode.props!.value)
} else if (value !== oldValue) {
el.checked = looseEqual(value, getCheckboxValue(el, true))
}
function setSelected(el: HTMLSelectElement, value: any) {
const isMultiple = el.multiple
- if (isMultiple && !isArray(value)) {
+ if (isMultiple && !isArray(value) && !isSet(value)) {
__DEV__ &&
warn(
- `<select multiple v-model> expects an Array value for its binding, ` +
+ `<select multiple v-model> expects an Array or Set value for its binding, ` +
`but got ${Object.prototype.toString.call(value).slice(8, -1)}.`
)
return
const option = el.options[i]
const optionValue = getValue(option)
if (isMultiple) {
- option.selected = looseIndexOf(value, optionValue) > -1
+ if (isArray(value)) {
+ option.selected = looseIndexOf(value, optionValue) > -1
+ } else {
+ option.selected = looseHas(value, optionValue)
+ }
} else {
if (looseEqual(getValue(option), value)) {
el.selectedIndex = i
if (vnode.props && looseIndexOf(value, vnode.props.value) > -1) {
return { checked: true }
}
+ } else if (isSet(value)) {
+ if (vnode.props && looseHas(value, vnode.props.value)) {
+ return { checked: true }
+ }
} else if (value) {
return { checked: true }
}
): key is keyof typeof val => hasOwnProperty.call(val, key)
export const isArray = Array.isArray
+export const isSet = (val: any): boolean => {
+ return toRawType(val) === 'Set'
+}
export const isDate = (val: unknown): val is Date => val instanceof Date
export const isFunction = (val: unknown): val is Function =>
typeof val === 'function'
export function looseIndexOf(arr: any[], val: any): number {
return arr.findIndex(item => looseEqual(item, val))
}
+
+export function looseHas(set: Set<any>, val: any): boolean {
+ for (let item of set) {
+ if (looseEqual(item, val)) return true
+ }
+ return false
+}