]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-dom): respect current select model type (#15010)
authorzongxi1115 <107086465+zongxi1115@users.noreply.github.com>
Thu, 16 Jul 2026 00:16:35 +0000 (08:16 +0800)
committerGitHub <noreply@github.com>
Thu, 16 Jul 2026 00:16:35 +0000 (08:16 +0800)
close #15009

packages/runtime-dom/__tests__/directives/vModel.spec.ts
packages/runtime-dom/src/directives/vModel.ts

index 2b78b0c617bc7b61a60c314ff11b6229f2287beb..590c0d7df27a527de3023b91c493789cbf443f08 100644 (file)
@@ -1317,6 +1317,54 @@ describe('vModel', () => {
     expect(bar.selected).toEqual(true)
   })
 
+  it('multiple select uses current Array/Set model type', async () => {
+    const component = defineComponent({
+      data() {
+        return { value: [] }
+      },
+      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
+
+    data.value = new Set(['foo'])
+    await nextTick()
+    foo.selected = true
+    bar.selected = true
+    triggerEvent('change', input)
+    await nextTick()
+    expect(data.value).toBeInstanceOf(Set)
+    expect(data.value).toMatchObject(new Set(['foo', 'bar']))
+
+    data.value = ['foo']
+    await nextTick()
+    foo.selected = false
+    bar.selected = true
+    triggerEvent('change', input)
+    await nextTick()
+    expect(Array.isArray(data.value)).toBe(true)
+    expect(data.value).toMatchObject(['bar'])
+  })
+
   it('multiple select (model is Set, option value is object)', async () => {
     const fooValue = { foo: 1 }
     const barValue = { bar: 1 }
index b49bf809e272c18078756f36510cc4ce27b8bc07..766db6bfe6c8aacc383f6ef04876ce7df9c55505 100644 (file)
@@ -207,7 +207,7 @@ export const vModelSelect: ModelDirective<HTMLSelectElement, 'number'> = {
   // <select multiple> value need to be deep traversed
   deep: true,
   created(el, { value, modifiers: { number } }, vnode) {
-    const isSetModel = isSet(value)
+    ;(el as any)._modelValue = value
     addEventListener(el, 'change', () => {
       const selectedVal = Array.prototype.filter
         .call(el.options, (o: HTMLOptionElement) => o.selected)
@@ -216,7 +216,7 @@ export const vModelSelect: ModelDirective<HTMLSelectElement, 'number'> = {
         )
       el[assignKey](
         el.multiple
-          ? isSetModel
+          ? isSet((el as any)._modelValue)
             ? new Set(selectedVal)
             : selectedVal
           : selectedVal[0],
@@ -233,7 +233,8 @@ export const vModelSelect: ModelDirective<HTMLSelectElement, 'number'> = {
   mounted(el, { value }) {
     setSelected(el, value)
   },
-  beforeUpdate(el, _binding, vnode) {
+  beforeUpdate(el, { value }, vnode) {
+    ;(el as any)._modelValue = value
     el[assignKey] = getModelAssigner(vnode)
   },
   updated(el, { value }) {