Reputation: 1455
First off, I am not a front-end dev, so please bear with me. I'm trying to upgrade a legacy project from Vue/Vuetify 2 to 3, and having issues with event handlers not discussed in the upgrade/migration guide.
I have this v-list:
<v-list density="compact" @update:selected="this.setSelected">
<v-list-item
v-for="item in items"
:key="item.title"
link
nav
:disabled="item.disabled"
:prepend-icon="item.icon || null"
:title="item.title"
:value="item.routeName"
/>
</v-list>
Later in the file:
<script>
import AppLayoutContent from '@/components/AppLayoutContent.vue'
export default {
name: 'AppLayout',
components: {
AppLayoutContent,
},
data: () => ({
items: [
{
title: 'Home',
icon: 'mdi-home',
routeName: 'Home'
},
{
title: 'Settings',
icon: 'mdi-cog-outline',
routeName: 'Settings',
}
]
}),
methods: {
setSelected(routeName) {
if (routeName && this.$route.name !== routeName) {
this.$router.push({ name: routeName[0] })
}
}
}
The active v-list-item would be the one where this.$route.name === routeName
is true.
I've tried all sorts of things, but nothing seems to work. This solution seems close, but I can't get it to work (possibly because they're using v-list-tile
and not v-list-item
?). I figure I probably have to do something with https://vuetifyjs.com/en/api/v-list/#props-selected , but there's zero documentation on how to use this
In the Vue2 version, we had
computed: {
selected: {
set(selected) {
const { routeName } = this.items[selected] || {}
if (routeName && this.$route.name !== routeName) {
this.$router.push({ name: routeName })
}
},
get() {
return findIndex(
this.items,
({ routeName }) => this.$route.name === routeName
)
},
},
},
Upvotes: 0
Views: 5188
Reputation: 15816
If you use the :to
prop to create the links, Vuetify will handle activation for you automatically (you can change the class for active items with the :active-class
prop).
So you could just do:
<v-list-item
v-for="item in items"
:to="{name: item.routeName}"
...
/>
Here it is in a snippet:
const { createApp, ref } = Vue
const { createVuetify } = Vuetify
const { createRouter, createWebHashHistory } = VueRouter
const vuetify = createVuetify()
const app = {
data: () => ({
items: [
{
title: 'Home',
icon: 'mdi-home',
routeName: 'Home'
},
{
title: 'Settings',
icon: 'mdi-cog-outline',
routeName: 'Settings',
}
]
}),
}
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: '/', name:'Home', component: app },
{ path: '/', name:'Settings', component: app },
]
})
createApp(app).use(vuetify).use(router).mount('#app')
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.css" />
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
<div id="app">
<v-app>
<v-main>
<v-card class="ma-4">
<v-list density="compact">
<v-list-item
v-for="item in items"
:key="item.title"
link
nav
:disabled="item.disabled"
:prepend-icon="item.icon || null"
:title="item.title"
:value="item.routeName"
:to="{name: item.routeName}"
></v-list-item>
</v-list>
</v-card>
</v-main>
</v-app>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.js"></script>
<script src=" https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.global.min.js "></script>
If you want to set the active state manually, v-list-item has an :active
prop that highlights the item if the provided value is true. So in your case that would give you:
<v-list-item
v-for="item in items"
:active="item.routeName === $route.name"
...
/>
Have a look at the snippet to see how it looks
const { createApp, ref } = Vue;
const { createVuetify } = Vuetify
const vuetify = createVuetify()
const app = {
data: () => ({
activeItem: null,
items: [
{
title: 'Home',
icon: 'mdi-home',
routeName: 'Home'
},
{
title: 'Settings',
icon: 'mdi-cog-outline',
routeName: 'Settings',
}
]
}),
methods:{
onSelect(selection){
const [itemTitle] = selection
this.activeItem = !itemTitle ? null : this.items.find(item => item.title === itemTitle)
}
}
}
createApp(app).use(vuetify).mount('#app')
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.css" />
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
<div id="app">
<v-app>
<v-main>
<v-card class="ma-4">
<v-list density="compact" @update:selected="onSelect">
<v-list-item
v-for="item in items"
:key="item.title"
link
nav
:disabled="item.disabled"
:prepend-icon="item.icon || null"
:title="item.title"
:value="item.routeName"
:active="activeItem?.routeName === item.routeName"
></v-list-item>
</v-list>
</v-card>
Active item: {{activeItem ?? 'none'}}
</v-main>
</v-app>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.js"></script>
Upvotes: 5