SelectMenu emits invalid native Event on @change (missing target.value)
Environment
- Operating System:
Darwin - Node Version:
v22.18.0 - Nuxt Version:
3.18.1 - CLI Version:
3.27.0 - Nitro Version:
2.11.13 - Package Manager:
[email protected] - Builder:
- - User Config:
devServer,typescript,compatibilityDate,ssr,modules,colorMode,devtools,app,imports,nitro,sourcemap,auth,runtimeConfig,vite,pwa,experimental,dayjs,css,ui - Runtime Modules:
@nuxt/[email protected],[email protected],@vueuse/[email protected],@sidebase/[email protected],@nuxt/[email protected],@vite-pwa/[email protected],[email protected] - Build Modules:
-
Is this bug related to Nuxt or Vue?
Nuxt
Package
v3.x
Version
v3.2.0
Reproduction
Just exec: new Event('change', { target: { value: 'foo' } }) as the source code does here.
The created event has a null target (and obviously no accessible value).
Description
When listening to the @change event emitted by SelectMenu, the handler receives a native Event instance that has no usable payload — event.target is null, and therefore event.target.value is undefined.
The component currently creates the event using:
const event = new Event('change', { target: { value } })
emit('change', event)
However, the second argument of the Event constructor only accepts { bubbles, cancelable, composed }.
As a result, the target property is ignored, and the emitted event is essentially empty.
Expected behavior
The @change event should provide access to the selected value or a proper payload.
Additional notes
In a sense, this seems like a known bug — or perhaps even an intentional behavior (given the @ts-expect-error) — however, I don’t see the point of emitting an event that’s effectively useless.
Moreover, my (legitimate, I believe) intent was to distinguish an event triggered by user interaction from a value change caused by other factors. There also seems to be (at least as far as I can tell) an intention in the source code to give the change event this specific meaning:
if (toRaw(props.modelValue) === value) {
return
}
but in practice, the event ends up being useless — it can’t even be used as a simple trigger to read the model value, since the model hasn’t been updated yet at that point.
Additional context
No response
Logs
The bug is valid, but I have to admit I misunderstood one point.
It seems that the update:modelValue event is also emitted only as a result of user actions, meaning that a programmatic change to the model does not trigger the event.
In that sense, it should be equivalent to the change event.
Could you please confirm this point? Thank you.
In this example, reset does not emit update:modelValue:
<script setup lang="ts">
const people = [
'Wade Cooper',
'Arlene Mccoy',
'Devon Webb',
'Tom Cook',
'Tanya Fox',
'Hellen Schmidt',
'Caroline Schultz',
'Mason Heaney',
'Claudie Smitham',
'Emil Schaefer',
];
const selected = ref(people[0]);
const onChange = (value: any): void => {
console.log('change', value);
};
const onUpdateModel = (value: any): void => {
console.log('updateModel', value);
};
const onReset = (): void => {
selected.value = people[0];
};
</script>
<template>
<div>
<USelectMenu v-model="selected" :items="people" @change="onChange" @update:model-value="onUpdateModel" />
<UButton label="reset" @click="onReset" />
</div>
</template>
I believe I have encountered this with the RadioGroup component as well where the change event's target is null due to this same line of code: https://github.com/nuxt/ui/blob/fda3c98ab798f045e6e3d781ec482ebe5f360c4e/src/runtime/components/RadioGroup.vue#L165
Looks like this may be an issue on several components that have that exact same line of code in them. I can't get the change event to bring back any data in the event.target object so listening for that event seems useless at the moment.
My specific problem with this relates to a 3rd party library (vee-validate) that is also listening to the value being passed into the v-model prop and when it changes it starts to run validation silently even though I don't want it to. With the Input component, I can listen to the @input event and bind the value to model-value="value" and based on this handle everything myself without issue.