Skip to content

Commit

Permalink
perf(runtime-dom/vModel): remove looseHas if model is Set (#2236)
Browse files Browse the repository at this point in the history
  • Loading branch information
Picknight authored Oct 13, 2020
1 parent 6b8cf99 commit 6a554fe
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 18 deletions.
202 changes: 201 additions & 1 deletion packages/runtime-dom/__tests__/directives/vModel.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ describe('vModel', () => {
expect(bar.selected).toEqual(true)
})

it('should work with multiple select', async () => {
it('multiple select (model is Array)', async () => {
const component = defineComponent({
data() {
return { value: [] }
Expand Down Expand Up @@ -783,6 +783,206 @@ describe('vModel', () => {
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 () => {
const component = defineComponent({
data() {
Expand Down
17 changes: 7 additions & 10 deletions packages/runtime-dom/src/directives/vModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import {
looseIndexOf,
invokeArrayFns,
toNumber,
isSet,
looseHas
isSet
} from '@vue/shared'

type AssignerFn = (value: any) => void
Expand Down Expand Up @@ -119,12 +118,10 @@ export const vModelCheckbox: ModelDirective<HTMLInputElement> = {
assign(filtered)
}
} else if (isSet(modelValue)) {
const found = modelValue.has(elementValue)
if (checked && !found) {
assign(modelValue.add(elementValue))
} else if (!checked && found) {
if (checked) {
modelValue.add(elementValue)
} else {
modelValue.delete(elementValue)
assign(modelValue)
}
} else {
assign(getCheckboxValue(el, checked))
Expand All @@ -148,7 +145,7 @@ function setChecked(
if (isArray(value)) {
el.checked = looseIndexOf(value, vnode.props!.value) > -1
} else if (isSet(value)) {
el.checked = looseHas(value, vnode.props!.value)
el.checked = value.has(vnode.props!.value)
} else if (value !== oldValue) {
el.checked = looseEqual(value, getCheckboxValue(el, true))
}
Expand Down Expand Up @@ -213,7 +210,7 @@ function setSelected(el: HTMLSelectElement, value: any) {
if (isArray(value)) {
option.selected = looseIndexOf(value, optionValue) > -1
} else {
option.selected = looseHas(value, optionValue)
option.selected = value.has(optionValue)
}
} else {
if (looseEqual(getValue(option), value)) {
Expand Down Expand Up @@ -305,7 +302,7 @@ if (__NODE_JS__) {
return { checked: true }
}
} else if (isSet(value)) {
if (vnode.props && looseHas(value, vnode.props.value)) {
if (vnode.props && value.has(vnode.props.value)) {
return { checked: true }
}
} else if (value) {
Expand Down
7 changes: 0 additions & 7 deletions packages/shared/src/looseEqual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,3 @@ export function looseEqual(a: any, b: any): boolean {
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
}

0 comments on commit 6a554fe

Please sign in to comment.