Inertia: onSelect in dropdown items stop working
Environment
- Operating System: Darwin
- Node Version: v22.14.0
- Nuxt Version: -
- CLI Version: 3.25.0
- Nitro Version: -
- Package Manager: [email protected]
- Builder: -
- User Config: -
- Runtime Modules: -
- Build Modules: -
Is this bug related to Nuxt or Vue?
Vue
Version
3.1.0
Reproduction
NA
Description
I’m using Vue 3 with Inertia. I upgraded to v3.1 and now it behaves strangely: components—like a dropdown menu inside a button group—redirect instead of honoring the onSelect and performing the intended actions. In my case, I want to update a ref when clicking a dropdown item and open a drawer containing a form.
Additional context
Laravel 12 with vue 3 and nuxtui 3.1
Logs
i am experiencing the same issue, so far I haven't found a solution just yet. any button behaves like a link and subsequently refreshes the current page.
using e.preventDefault() or @click.prevent(() => {}) don't help either.
Same stack. Nuxt UI Pro 3.1, Laravel 11, and Inertia 2.0.7.
Same here. Somehow all buttons now render as nested anchor tags with href="#".
DId you guys set the inertia option? 🤔 https://ui.nuxt.com/getting-started/installation/vue#inertia
Yes I did, yet all buttons now are rendered like this:
<a custom href="">
<a href='#' class='...'>Hello</a>
</a>
this happens even when I pass the type='button' and as='button' props. These props seem to be ignored completely.
yep. the same here. all the buttons render as anchor tag
Yes I did, yet all buttons now are rendered like this:
<a custom href=""> <a href='#' class='...'>Hello</a> </a>this happens even when I pass the
type='button'andas='button'props. These props seem to be ignored completely.
As a result, the month change in the calendar component no longer works.
It appears that the url is always defined at this line, causing the component to render as an a tag by default
https://github.com/nuxt/ui/blob/9f7f5910ee3374a413bb8e5a889a9aa31fff9ae3/src/runtime/inertia/components/Link.vue#L113
Additionally, the InertiaLink component is consistently rendered, leading to full page reloads even when a simple button behavior is intended.
It appears that the url is always defined at this line, causing the component to render as an
atag by defaultui/src/runtime/inertia/components/Link.vue
Line 113 in 9f7f591
const url = computed(() => props.to ?? props.href ?? '#') Additionally, the InertiaLink component is consistently rendered, leading to full page reloads even when a simple button behavior is intended.
indeed, I cloned the repo locally and it does appear that for buttons, replacing # with null solves the problem for buttons.
however, it doesn't seem to be doing anything for dropdowns (i.e the one in the dashboard sidebar).
https://github.com/nuxt/ui/blob/9f7f5910ee3374a413bb8e5a889a9aa31fff9ae3/src/runtime/inertia/components/Link.vue#L118-L132
I think that, the issue arises because the InertiaLink component is rendered even when the url is null. In such cases, it attempts to navigate, potentially leading to unexpected behavior like linking to the current page or triggering navigation events.
I created a PR #3989 for this issue.
Since the InertiaLink component is not implemented as a renderless component, the custom flag cannot be utilized. Similarly, LinkBase.vue should also be separated using unplugin. Consequently, the LinkBase.vue file will need to be updated. Below is a simple code draft I put together; please note that it has not yet been tested.
As for the Link.vue file, only specific sections have been modified.
- inertia/components/Link.vue
...
<template v-if="!isExternal">
<slot
v-if="custom"
v-bind="{
...routerLinkProps,
...$attrs,
as,
type,
disabled,
href: url,
active: isActive
}"
/>
<ULinkBase
v-else
v-bind="{
...routerLinkProps,
...$attrs,
as,
type,
disabled,
href: url,
active: isActive
}"
:class="linkClass"
>
<slot :active="isActive" />
</ULinkBase>
</template>
...
- inertia/components/LinkBase.vue
<script setup>
import { Link as InertiaLink } from '@inertiajs/vue3'
import { Primitive } from 'reka-ui'
import { computed, useAttrs } from 'vue'
const props = defineProps({
as: { type: String, default: 'button' },
type: { type: String, default: 'button' },
disabled: Boolean,
onClick: [Function, Array],
href: String,
navigate: Function,
target: [String, Object, null],
rel: [String, Object, null],
active: Boolean,
isExternal: Boolean,
})
const attrs = useAttrs()
function onClickWrapper(e) {
if (props.disabled) {
e.preventDefault()
return
}
const handlers = Array.isArray(props.onClick) ? props.onClick : [props.onClick]
handlers.forEach(fn => fn?.(e))
if (props.href && props.navigate && !props.isExternal) {
props.navigate(e)
}
}
const componentProps = computed(() => {
if (props.href) {
const base = {
'href': props.disabled ? undefined : props.href,
'aria-disabled': props.disabled ? 'true' : void 0,
'role': props.disabled ? 'link' : void 0,
'tabindex': props.disabled ? -1 : void 0,
}
let isExternal = props.isExternal
// optionally, Do you want automatically detect external links?
if (props.href.startsWith('http') && typeof window !== 'undefined') {
const url = new URL(props.href)
isExternal = url.host === window.location.host
}
return isExternal || props.disabled
? { as: 'a', ...base }
: { as: InertiaLink, ...base, ...attrs }
}
if (props.as === 'button') {
return { as: 'button', type: props.type, disabled: props.disabled }
}
return { as: props.as }
})
</script>
<template>
<Primitive
v-bind="componentProps"
:rel="rel"
:target="target"
@click="onClickWrapper"
>
<slot />
</Primitive>
</template>
@romhml @benjamincanac Could this issue be reopened?
@benjamincanac I tested out the latest pkg.pr.new/@nuxt/ui@1fa5e5b with this fix but the onSelect issue still persists for me. Did you get it working on your end with this fix?
@Mallon94 Really? Yes I merged my PR because everything was working fine 🤔 Did you set the @nuxt/ui resolution correctly?
{
"resolutions": {
"@nuxt/ui": "https://pkg.pr.new/@nuxt/ui@b9adc83"
}
}
@benjamincanac apologies, i used the wrong package. This is working. Thank you very much!