perf(runtime-dom/vModel): remove looseHas if model is Set (#2236)
This commit is contained in:
parent
6b8cf998c4
commit
6a554feb13
@ -619,7 +619,7 @@ describe('vModel', () => {
|
|||||||
expect(bar.selected).toEqual(true)
|
expect(bar.selected).toEqual(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should work with multiple select', async () => {
|
it('multiple select (model is Array)', async () => {
|
||||||
const component = defineComponent({
|
const component = defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return { value: [] }
|
return { value: [] }
|
||||||
@ -783,6 +783,206 @@ describe('vModel', () => {
|
|||||||
expect(two.selected).toEqual(true)
|
expect(two.selected).toEqual(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('multiple select (model is Array, option value is object)', async () => {
|
||||||
|
const fooValue = { foo: 1 }
|
||||||
|
const barValue = { bar: 1 }
|
||||||
|
|
||||||
|
const component = defineComponent({
|
||||||
|
data() {
|
||||||
|
return { value: [] }
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return [
|
||||||
|
withVModel(
|
||||||
|
h(
|
||||||
|
'select',
|
||||||
|
{
|
||||||
|
value: null,
|
||||||
|
multiple: true,
|
||||||
|
'onUpdate:modelValue': setValue.bind(this)
|
||||||
|
},
|
||||||
|
[
|
||||||
|
h('option', { value: fooValue }),
|
||||||
|
h('option', { value: barValue })
|
||||||
|
]
|
||||||
|
),
|
||||||
|
this.value
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
render(h(component), root)
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const input = root.querySelector('select')
|
||||||
|
const [foo, bar] = root.querySelectorAll('option')
|
||||||
|
const data = root._vnode.component.data
|
||||||
|
|
||||||
|
foo.selected = true
|
||||||
|
triggerEvent('change', input)
|
||||||
|
await nextTick()
|
||||||
|
expect(data.value).toMatchObject([fooValue])
|
||||||
|
|
||||||
|
foo.selected = false
|
||||||
|
bar.selected = true
|
||||||
|
triggerEvent('change', input)
|
||||||
|
await nextTick()
|
||||||
|
expect(data.value).toMatchObject([barValue])
|
||||||
|
|
||||||
|
foo.selected = true
|
||||||
|
bar.selected = true
|
||||||
|
triggerEvent('change', input)
|
||||||
|
await nextTick()
|
||||||
|
expect(data.value).toMatchObject([fooValue, barValue])
|
||||||
|
|
||||||
|
foo.selected = false
|
||||||
|
bar.selected = false
|
||||||
|
data.value = [fooValue, barValue]
|
||||||
|
await nextTick()
|
||||||
|
expect(foo.selected).toEqual(true)
|
||||||
|
expect(bar.selected).toEqual(true)
|
||||||
|
|
||||||
|
foo.selected = false
|
||||||
|
bar.selected = false
|
||||||
|
data.value = [{ foo: 1 }, { bar: 1 }]
|
||||||
|
await nextTick()
|
||||||
|
// looseEqual
|
||||||
|
expect(foo.selected).toEqual(true)
|
||||||
|
expect(bar.selected).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('multiple select (model is Set)', async () => {
|
||||||
|
const component = defineComponent({
|
||||||
|
data() {
|
||||||
|
return { value: new Set() }
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return [
|
||||||
|
withVModel(
|
||||||
|
h(
|
||||||
|
'select',
|
||||||
|
{
|
||||||
|
value: null,
|
||||||
|
multiple: true,
|
||||||
|
'onUpdate:modelValue': setValue.bind(this)
|
||||||
|
},
|
||||||
|
[h('option', { value: 'foo' }), h('option', { value: 'bar' })]
|
||||||
|
),
|
||||||
|
this.value
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
render(h(component), root)
|
||||||
|
|
||||||
|
const input = root.querySelector('select')
|
||||||
|
const foo = root.querySelector('option[value=foo]')
|
||||||
|
const bar = root.querySelector('option[value=bar]')
|
||||||
|
const data = root._vnode.component.data
|
||||||
|
|
||||||
|
foo.selected = true
|
||||||
|
triggerEvent('change', input)
|
||||||
|
await nextTick()
|
||||||
|
expect(data.value).toMatchObject(new Set(['foo']))
|
||||||
|
|
||||||
|
foo.selected = false
|
||||||
|
bar.selected = true
|
||||||
|
triggerEvent('change', input)
|
||||||
|
await nextTick()
|
||||||
|
expect(data.value).toMatchObject(new Set(['bar']))
|
||||||
|
|
||||||
|
foo.selected = true
|
||||||
|
bar.selected = true
|
||||||
|
triggerEvent('change', input)
|
||||||
|
await nextTick()
|
||||||
|
expect(data.value).toMatchObject(new Set(['foo', 'bar']))
|
||||||
|
|
||||||
|
foo.selected = false
|
||||||
|
bar.selected = false
|
||||||
|
data.value = new Set(['foo'])
|
||||||
|
await nextTick()
|
||||||
|
expect(input.value).toEqual('foo')
|
||||||
|
expect(foo.selected).toEqual(true)
|
||||||
|
expect(bar.selected).toEqual(false)
|
||||||
|
|
||||||
|
foo.selected = false
|
||||||
|
bar.selected = false
|
||||||
|
data.value = new Set(['foo', 'bar'])
|
||||||
|
await nextTick()
|
||||||
|
expect(foo.selected).toEqual(true)
|
||||||
|
expect(bar.selected).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('multiple select (model is Set, option value is object)', async () => {
|
||||||
|
const fooValue = { foo: 1 }
|
||||||
|
const barValue = { bar: 1 }
|
||||||
|
|
||||||
|
const component = defineComponent({
|
||||||
|
data() {
|
||||||
|
return { value: new Set() }
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return [
|
||||||
|
withVModel(
|
||||||
|
h(
|
||||||
|
'select',
|
||||||
|
{
|
||||||
|
value: null,
|
||||||
|
multiple: true,
|
||||||
|
'onUpdate:modelValue': setValue.bind(this)
|
||||||
|
},
|
||||||
|
[
|
||||||
|
h('option', { value: fooValue }),
|
||||||
|
h('option', { value: barValue })
|
||||||
|
]
|
||||||
|
),
|
||||||
|
this.value
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
render(h(component), root)
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const input = root.querySelector('select')
|
||||||
|
const [foo, bar] = root.querySelectorAll('option')
|
||||||
|
const data = root._vnode.component.data
|
||||||
|
|
||||||
|
foo.selected = true
|
||||||
|
triggerEvent('change', input)
|
||||||
|
await nextTick()
|
||||||
|
expect(data.value).toMatchObject(new Set([fooValue]))
|
||||||
|
|
||||||
|
foo.selected = false
|
||||||
|
bar.selected = true
|
||||||
|
triggerEvent('change', input)
|
||||||
|
await nextTick()
|
||||||
|
expect(data.value).toMatchObject(new Set([barValue]))
|
||||||
|
|
||||||
|
foo.selected = true
|
||||||
|
bar.selected = true
|
||||||
|
triggerEvent('change', input)
|
||||||
|
await nextTick()
|
||||||
|
expect(data.value).toMatchObject(new Set([fooValue, barValue]))
|
||||||
|
|
||||||
|
foo.selected = false
|
||||||
|
bar.selected = false
|
||||||
|
data.value = new Set([fooValue, barValue])
|
||||||
|
await nextTick()
|
||||||
|
expect(foo.selected).toEqual(true)
|
||||||
|
expect(bar.selected).toEqual(true)
|
||||||
|
|
||||||
|
foo.selected = false
|
||||||
|
bar.selected = false
|
||||||
|
data.value = new Set([{ foo: 1 }, { bar: 1 }])
|
||||||
|
await nextTick()
|
||||||
|
// whithout looseEqual, here is different from Array
|
||||||
|
expect(foo.selected).toEqual(false)
|
||||||
|
expect(bar.selected).toEqual(false)
|
||||||
|
})
|
||||||
|
|
||||||
it('should work with composition session', async () => {
|
it('should work with composition session', async () => {
|
||||||
const component = defineComponent({
|
const component = defineComponent({
|
||||||
data() {
|
data() {
|
||||||
|
@ -12,8 +12,7 @@ import {
|
|||||||
looseIndexOf,
|
looseIndexOf,
|
||||||
invokeArrayFns,
|
invokeArrayFns,
|
||||||
toNumber,
|
toNumber,
|
||||||
isSet,
|
isSet
|
||||||
looseHas
|
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
|
|
||||||
type AssignerFn = (value: any) => void
|
type AssignerFn = (value: any) => void
|
||||||
@ -119,12 +118,10 @@ export const vModelCheckbox: ModelDirective<HTMLInputElement> = {
|
|||||||
assign(filtered)
|
assign(filtered)
|
||||||
}
|
}
|
||||||
} else if (isSet(modelValue)) {
|
} else if (isSet(modelValue)) {
|
||||||
const found = modelValue.has(elementValue)
|
if (checked) {
|
||||||
if (checked && !found) {
|
modelValue.add(elementValue)
|
||||||
assign(modelValue.add(elementValue))
|
} else {
|
||||||
} else if (!checked && found) {
|
|
||||||
modelValue.delete(elementValue)
|
modelValue.delete(elementValue)
|
||||||
assign(modelValue)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assign(getCheckboxValue(el, checked))
|
assign(getCheckboxValue(el, checked))
|
||||||
@ -148,7 +145,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 (isSet(value)) {
|
} else if (isSet(value)) {
|
||||||
el.checked = looseHas(value, vnode.props!.value)
|
el.checked = value.has(vnode.props!.value)
|
||||||
} else if (value !== oldValue) {
|
} else if (value !== oldValue) {
|
||||||
el.checked = looseEqual(value, getCheckboxValue(el, true))
|
el.checked = looseEqual(value, getCheckboxValue(el, true))
|
||||||
}
|
}
|
||||||
@ -213,7 +210,7 @@ function setSelected(el: HTMLSelectElement, value: any) {
|
|||||||
if (isArray(value)) {
|
if (isArray(value)) {
|
||||||
option.selected = looseIndexOf(value, optionValue) > -1
|
option.selected = looseIndexOf(value, optionValue) > -1
|
||||||
} else {
|
} else {
|
||||||
option.selected = looseHas(value, optionValue)
|
option.selected = value.has(optionValue)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (looseEqual(getValue(option), value)) {
|
if (looseEqual(getValue(option), value)) {
|
||||||
@ -305,7 +302,7 @@ if (__NODE_JS__) {
|
|||||||
return { checked: true }
|
return { checked: true }
|
||||||
}
|
}
|
||||||
} else if (isSet(value)) {
|
} else if (isSet(value)) {
|
||||||
if (vnode.props && looseHas(value, vnode.props.value)) {
|
if (vnode.props && value.has(vnode.props.value)) {
|
||||||
return { checked: true }
|
return { checked: true }
|
||||||
}
|
}
|
||||||
} else if (value) {
|
} else if (value) {
|
||||||
|
@ -51,10 +51,3 @@ export function looseEqual(a: any, b: any): boolean {
|
|||||||
export function looseIndexOf(arr: any[], val: any): number {
|
export function looseIndexOf(arr: any[], val: any): number {
|
||||||
return arr.findIndex(item => looseEqual(item, val))
|
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
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user