英文:
Vuetify 3 v-list-group - collapse other sub-menus when opening group
问题
I'm using Vuetify 3 and I'm trying to implement a collapsible vertical menu inside the Vuetify navigation drawer using the v-list-group component. I need the currently expanded menu item to collapse when another menu item is clicked to expand (Accordion behavior). I've created the menu list, but the accordion effect does not work as expected.
Template:
<v-navigation-drawer
v-model="drawer"
temporary
location="right"
theme="dark"
>
<v-list density="compact">
<v-list-group v-model="item.active" v-for="item in menuItems">
<template v-slot:activator="{ props }">
<v-list-item
:key="item.title"
v-bind="props"
:title="item.title"
></v-list-item>
</template>
<v-list-item
v-for="subMenu in item.subMenuItems"
:key="subMenu"
:title="subMenu"
></v-list-item>
</v-list-group>
</v-list>
</v-navigation-drawer>
Script:
<script setup>
const menuItems = ref([
{
title: "Menu Item 1",
active: false,
subMenuItems: ["Sub Menu Item 1", "Sub Menu Item 2", "Sub Menu Item 3"]
},
{
title: "Menu Item 2",
active: false,
subMenuItems: ["Sub Menu Item 1", "Sub Menu Item 2", "Sub Menu Item 3"]
},
{
title: "Menu Item 3",
active: false,
subMenuItems: ["Sub Menu Item 1", "Sub Menu Item 2", "Sub Menu Item 3"]
}
])
</script>
In Vuetify 2, I could assign a variable to the v-model
of v-list-group
component, but this does not work with Vuetify 3 (Changing the value does not change the collapse/expand state).
The v-list-group
component does not contain an is-open
prop to manually modify its state (there is an isOpen
flag which is passed to the activator slot, but it is read-only).
英文:
I'm using Vuetify 3 and I'm trying to implement a collapsible vertical menu inside the Vuetify navigation drawer using the v-list-group component. I need the currently expanded menu item to collapse when another menu item is clicked to expand (Accordion behavior). I've created the menu list, but the accordion effect does not work as expected.
Template:
<v-navigation-drawer
v-model="drawer"
temporary
location="right"
theme="dark"
>
<v-list density="compact">
<v-list-group v-model="item.active" v-for="item in menuItems">
<template v-slot:activator="{ props }">
<v-list-item
:key="item.title"
v-bind="props"
:title="item.title"
></v-list-item>
</template>
<v-list-item
v-for="subMenu in item.subMenuItems"
:key="subMenu"
:title="subMenu"
></v-list-item>
</v-list-group>
</v-list>
</v-navigation-drawer>
Script:
<script setup>
const menuItems = ref([
{
title: "Menu Item 1",
active: false,
subMenuItems: ["Sub Menu Item 1", "Sub Menu Item 2", "Sub Menu Item 3"]
},
{
title: "Menu Item 2",
active: false,
subMenuItems: ["Sub Menu Item 1", "Sub Menu Item 2", "Sub Menu Item 3"]
},
{
title: "Menu Item 3",
active: false,
subMenuItems: ["Sub Menu Item 1", "Sub Menu Item 2", "Sub Menu Item 3"]
}
])
</script>
In Vuetify 2, I could assign a variable to the v-model
of v-list-group
component, but this does not work with Vuetify 3 (Changing the value does not change the collapse/expand state).
The v-list-group
component does not contain an is-open
prop to manually modify its state (there is an isOpen
flag which is passed to the activator slot, but it is read-only).
答案1
得分: 3
以下是代码部分的翻译:
<v-list
:opened="opened"
@update:opened="newOpened => opened = newOpened.slice(-1)"
>
<v-list-group
v-for="item in menuItems"
:key="item.title"
:value="item"
>
...
const {
createApp,
ref,
computed
} = Vue;
const {
createVuetify
} = Vuetify
const vuetify = createVuetify()
createApp({
setup() {
const menuItems = ref([{
title: "Menu Item 1",
active: false,
subMenuItems: ["Sub Menu Item 1", "Sub Menu Item 2", "Sub Menu Item 3"]
},
{
title: "Menu Item 2",
active: true,
subMenuItems: ["Sub Menu Item 1", "Sub Menu Item 2", "Sub Menu Item 3"]
},
{
title: "Menu Item 3",
active: false,
subMenuItems: ["Sub Menu Item 1", "Sub Menu Item 2", "Sub Menu Item 3"]
}
])
const opened = ref([])
return {
opened,
drawer: ref(true),
menuItems,
}
}
}).use(vuetify).mount('#app')
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/vuetify@3.1.9/dist/vuetify.min.css" />
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css" rel="stylesheet">
<div id="app" class="d-flex justify-center">
<v-app>
<v-card>
<v-layout>
<v-navigation-drawer v-model="drawer" temporary>
<v-list density="compact" :opened="opened" @update:opened="opened = $event.slice(-1)">
<v-list-group v-for="item in menuItems" :key="item.title" :value="item">
<template v-slot:activator="{props, isOpen}">
<v-list-item
v-bind="props"
:key="item.title"
:title="item.title"
></v-list-item>
</template>
<v-list-item v-for="subMenu in item.subMenuItems" :key="subMenu" :title="subMenu"></v-list-item>
</v-list-group>
</v-list>
</v-navigation-drawer>
<v-main style="height: 500px;">
</v-main>
</v-layout>
</v-card>
</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.1.9/dist/vuetify.min.js"></script>
英文:
Yup, looks like that was changed, you need to do it using the surrounding v-list
now.
The :opened
property of v-list
is an array that corresponds to which groups are opened. Which values are put into the list is determined by the :value
property of v-list-group
. To have at most one group open, you can listen to the @update:opened
event and fix the list accordingly.
So with a ref opened = ref([])
, you can do:
<v-list
:opened="opened"
@update:opened="newOpened => opened = newOpened.slice(-1)"
>
<v-list-group
v-for="item in menuItems"
:key="item.title"
:value="item"
>
...
With @update:opened="opened = $event.slice(-1)"
, all but the last element will be removed when the list is updated. This will close all groups except the one clicked last.
The :value="item"
determines what will be put into the opened
array. It does not really matter what it is, as long as it is unique for each group (unless you want items to open and close together, then they should have the same value).
Here it is in a snippet:
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
const {
createApp,
ref,
computed
} = Vue;
const {
createVuetify
} = Vuetify
const vuetify = createVuetify()
createApp({
setup() {
const menuItems = ref([{
title: "Menu Item 1",
active: false,
subMenuItems: ["Sub Menu Item 1", "Sub Menu Item 2", "Sub Menu Item 3"]
},
{
title: "Menu Item 2",
active: true,
subMenuItems: ["Sub Menu Item 1", "Sub Menu Item 2", "Sub Menu Item 3"]
},
{
title: "Menu Item 3",
active: false,
subMenuItems: ["Sub Menu Item 1", "Sub Menu Item 2", "Sub Menu Item 3"]
}
])
const opened = ref([])
return {
opened,
drawer: ref(true),
menuItems,
}
}
}).use(vuetify).mount('#app')
<!-- language: lang-html -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/vuetify@3.1.9/dist/vuetify.min.css" />
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css" rel="stylesheet">
<div id="app" class="d-flex justify-center">
<v-app>
<v-card>
<v-layout>
<v-navigation-drawer v-model="drawer" temporary>
<v-list density="compact" :opened="opened" @update:opened="opened = $event.slice(-1)">
<v-list-group v-for="item in menuItems" :key="item.title" :value="item">
<template v-slot:activator="{props, isOpen}">
<v-list-item
v-bind="props"
:key="item.title"
:title="item.title"
></v-list-item>
</template>
<v-list-item v-for="subMenu in item.subMenuItems" :key="subMenu" :title="subMenu"></v-list-item>
</v-list-group>
</v-list>
</v-navigation-drawer>
<v-main style="height: 500px;">
</v-main>
</v-layout>
</v-card>
</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.1.9/dist/vuetify.min.js"></script>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论