vuetify dropdown v-menu with submenu don't close on select - vue.js

Working with Vuetify and have problem with the submenu to a a dropdown menu.
Everything works as it should, except for the main dropdown menu that does not closes when click on a submenu item. The submenu closes as it should.
1. The dropdown menu open on click
2. The submenu open on hover
3. If I click on a main menu item, the whole menu close. I want it to stay open as I don't have any router link for the main menu items, only for the submenu items.
4. If I click on a submenu item, I get routed to the new page, but the main menu does not close, only the submenu. Have to click a second time outside the dropdown box to close it.
I have tried with "close-on-click" and "close-on-content-click" without sucsess.
<v-menu offset-y :close-on-select="true">
<v-btn flat slot="activator">
<v-icon left>expand_more</v-icon>
<span>Our Adventures</span>
</v-btn>
<v-list class="py-0">
<v-list-tile>
<router-link to="/adventures">
<v-list-tile-title class="black--text plain-text">All our adventures</v-list-tile-title>
</router-link>
</v-list-tile>
</v-list>
<v-list v-for="item in items" :key="item.title" class="text-xs-left py-0">
<v-menu offset-x right open-on-hover>
<v-list-tile slot="activator">
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
<v-list dense>
<v-list-tile
v-for="subItem in item.items"
:key="subItem.title"
#click="close"
router
:to="subItem.link"
>
<v-list-tile-title>{{ subItem.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</v-list>
</v-menu>
and the related script
items: [
{
title: "Nordic skating",
items: [
{ title: "Open tour", link: "/adventures/skating/weekend" },
{ title: "Private tour", link: "/adventures/skating/private" }
]
},
{
title: "Kayak",
items: [
{ title: "Open tour", link: "/adventures/kayak/weekend" },
{ title: "Private tour", link: "/adventures/kayak/private" }
]
},
{
title: "Hiking",
items: [
{ title: "Open tour", link: "/adventures/hiking/eightdays" },
{ title: "Private tour", link: "/adventures/hiking/private" }
]
},
{
title: "Cross country skiing",
items: [
{ title: "Open tour", link: "/adventures/skiing/weekend" },
{ title: "Private tour", link: "/adventures/skiing/private" },
{
title: "Winter adventures",
link: "/adventures/skiing/adventures"
}
]
}
],

Solved the issue of parentMenu not closing by using ref and the isActive property.
Steps:
Add ref = "parentMenuRef" to the parent v-menu
In the childMenu items, add #click="$refs.parentMenuRef.isActive = false"
This will close the parentMenu along with the childMenu when the childMenu item is clicked. Original answer

Remove the "open-on-hover" then it will work as it should be. Open-on-hover gives effect close window on 2 time click. I had same issue and no success. I would suggest you to custom menu instead of Vuetify menu.

You have not mentioned Vuetify version, but I assume it's 1.x.
Here's what I have done in my projects:
In top level v-menu, close-on-content-click="true". This prop is true by default, so, you don't need to add it.
The inner v-menu will have open-on-hover, which you code already has.
Moved the slot="activator" to a template.
On the activator of inner v-menu, I have added #click.stop.prevent
So, your code should look like:
<v-menu offset-y>
<v-btn flat slot="activator">
<v-icon left>expand_more</v-icon>
<span>Our Adventures</span>
</v-btn>
<v-list class="py-0">
<v-list-tile>
<router-link to="/adventures">
<v-list-tile-title class="black--text plain-text">All our adventures</v-list-tile-title>
</router-link>
</v-list-tile>
</v-list>
<v-list v-for="item in items" :key="item.title" class="text-xs-left py-0">
<v-menu offset-x right open-on-hover>
<template slot="activator">
<v-list-tile
#click.stop.prevent
>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
<template>
<v-list dense>
<v-list-tile
v-for="subItem in item.items"
:key="subItem.title"
#click="close"
router
:to="subItem.link"
>
<v-list-tile-title>{{ subItem.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</v-list>
</v-menu>
Side Note: slot attribute is deprecated in Vue 2.6. Please consider using v-slot directive. https://v2.vuejs.org/v2/guide/components-slots.html

Related

vuetify v-list with reactive source collapses upon adding or removing items

not sure if the reactivity part is relevant, but I use vuetify with Meteor.js and my problem is that whenever the number of items in the sub-group changes, it collapses the entire list. That is extremely annoying as the list has two levels and I need to reopen both levels to get back to the group I am editing.
I use exactly the same structure as the official sub-group example:
https://vuetifyjs.com/en/components/lists/#sub-group
Say the number of items in the Admin section in that example above changes (which means reassigning a different array to a local variable in data(){}). Then the whole list will collapse.
Is there anything I can do to keep having opened the current item?
Thanks for any tips!
I tested [https://vuetifyjs.com/en/components/lists/#sub-group][1] in Vue 2 and for me, the menu didn't close when a new item was added.
I pushed a new item into data.admin
I assigned a totally new array to data.admin
In both cases, the menu remained open.
I think the question is, are you updating a component state or props.
Because updating component props would probably cause it to rerender and therefore close the menu.
Here is the code that worked for me:
<template>
<v-app>
<v-main>
<v-card class="mx-auto" width="300">
<v-list>
<v-list-group :value="true" prepend-icon="mdi-account-circle">
<template v-slot:activator>
<v-list-item-title>Users</v-list-item-title>
</template>
<v-list-group :value="true" no-action sub-group>
<template v-slot:activator>
<v-list-item-content>
<v-list-item-title>Admin</v-list-item-title>
</v-list-item-content>
</template>
<v-list-item v-for="([title, icon], i) in admins" :key="i" link>
<v-list-item-title v-text="title"></v-list-item-title>
<v-list-item-icon>
<v-icon v-text="icon"></v-icon>
</v-list-item-icon>
</v-list-item>
</v-list-group>
<v-list-group no-action sub-group>
<template v-slot:activator>
<v-list-item-content>
<v-list-item-title>Actions</v-list-item-title>
</v-list-item-content>
</template>
<v-list-item v-for="([title, icon], i) in cruds" :key="i" link>
<v-list-item-title v-text="title"></v-list-item-title>
<v-list-item-icon>
<v-icon v-text="icon"></v-icon>
</v-list-item-icon>
</v-list-item>
</v-list-group>
</v-list-group>
</v-list>
</v-card>
<v-btn #click="addElement">Add element</v-btn>
</v-main>
</v-app>
</template>
<script>
export default {
name: 'App',
data: () => ({
admins: [
['Management', 'mdi-account-multiple-outline'],
['Settings', 'mdi-cog-outline'],
],
cruds: [
['Create', 'mdi-plus-outline'],
['Read', 'mdi-file-outline'],
['Update', 'mdi-update'],
['Delete', 'mdi-delete'],
],
}),
methods: {
addElement() {
this.admins.push(['New', 'mdi-account-multiple-outline']);
// Completely change the menu
/* this.admins = [
['New', 'mdi-account-multiple-outline'],
['Management', 'mdi-account-multiple-outline'],
['Settings', 'mdi-cog-outline'],
]; */
},
},
};
</script>

vuetify navigation drawer cannot be clicked

I created a navigation drawer using Vuetify but none of the menus can be clicked.
Here's the code
<v-navigation-drawer v-model="drawer" temporary app class="primary">
<v-list>
<v-list-item v-for="link in links" :key="link.text" link :to="link.path">
<v-list-item-action>
<v-icon class="white--text">{{link.icon}}</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title class="white--text">{{link.text}}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
<template v-slot:append>
<div class="pa-2">
<v-btn block>
Sign out
<v-icon right>mdi-exit-to-app</v-icon>
</v-btn>
</div>
</template>
</v-navigation-drawer>
data() {
return {
drawer: false,
links: [
{icon: 'mdi-view-dashboard', text: 'Dashboard', path: '/'},
{icon: 'mdi-application-edit', text: 'Settings', path: '/settings'}
]
}
}
I have checked the routes in index.js and there is nothing wrong there. Also, if I go to /settings page, the Setting menu will be highlighted (active) but still cannot be clicked. How to fix this?
Solved it! The Navbar component was nested in a v-app-bar and somehow it affected the navbar. So I only removed the v-app-bar and the navigation drawer works perfectly fine!

Unable to change vuetify's card/navigation drawer's height and colour

I'm having a difficult time setting the height to match size of the browser window and setting the color of vuetify's card.
I've copied the example code from vuetiy as show below:
<template>
<v-card
class="mx-auto"
height="400"
width="256"
>
<v-navigation-drawer
class="deep-purple accent-4"
dark
permanent
>
<v-list>
<v-list-item
v-for="item in items"
:key="item.title"
link
>
<v-list-item-icon>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
<template v-slot:append>
<div class="pa-2">
<v-btn block>
Logout
</v-btn>
</div>
</template>
</v-navigation-drawer>
</v-card>
</template>
<script>
export default {
data () {
return {
items: [
{ title: 'Dashboard', icon: 'mdi-view-dashboard' },
{ title: 'Account', icon: 'mdi-account-box' },
{ title: 'Admin', icon: 'mdi-gavel' },
],
}
},
}
</script>
But the colour doesn't show up as purple. Instead it's showing as dark grey. If I remove the dark attribute in v-navigation-drawer then it just appears white.
Changing the v-card's height to 100% seems to just change the card's height to 100 instead of 100%. I'm really confused as from what I can see here, How to set the height of vuetify card, having
<v-card
height=100%
>
would have fixed the issue but it doesn't. I must be missing something simple?
So I did end up able to modify change the height and the colour although I'm not sure why it can't be done within the card tag.
In the navigation drawer I made some changes as shown below and the color as well as the height changed.
<v-navigation-drawer
class="accent-4"
width="200"
height="100vh"
color="rgba(56, 95, 115,0.5)"
permanent
>
I also removed the v-card tag completely.

v-list in v-list not working with vuetify

I am trying to implement list in list using vuetify. But I didnt manage to make it work. Below is the code and sample data. Please help me make it work. I am using vuetify 2.3.10. If I comment out the part for the second list then the first list works
<template>
<div>
<v-toolbar id="filter">
<v-layout row wrap>
<v-flex xs12 md1>
<v-menu attach="#filter-users" offset-y>
<template v-slot:activator="{ on, attrs }">
<div class="dropdown" v-bind="attrs" v-on="on">User</div>
</template>
<v-list id="filter-users">
<v-list-item
v-for="search_user in search_users"
#click="filterUser({user: search_user})">
<v-list-item-title>{{ search_user.name }}</v-list-item-title>
</v-list-item>
<v-list v-if="search_user.sub_users.length">
<v-list-item v-for="(search_sub_users, index) in search_user.sub_users"
:key="`sub_user_${index}`"
#click="filterUsers({user: search_sub_user})">
<v-list-item-title>{{ search_sub_user.name }}</v-list-item-title>
</v-list-item>
</v-list>
</v-list>
</v-menu>
</v-flex>
</v-layout>
</v-toolbar>
</div>
</template>
search_users demo data
search_users:[{
name: 'James',
sub_users: [
{
name: 'Willy'
},
{
name: 'Jack'
},
}]
},
name: 'Rock',
sub_users: [
{
name: 'Randy'
},
{
name: 'Amy'
},
}]
],
There's a lot of things going on in your code so let me enumerate them for you:
On your second sub-list, it can't find the search_user variable since that variable is only accessible inside the <v-list-item/>, which is outside of the <v-list/>.
What you can do is to create a <template/> that will wrap both <v-list-item/> and <v-list/> then put the v-for there. Something like this:
<v-list ...>
<template v-for="(search_user, index) in search_users">
<v-list-item :key="`user_${index}`">...</v-list-item> <!-- User List -->
<v-list :key="`sub_user_${index}`">...</v-list> <!-- Sub User List -->
</template>
</v-list>
Also, don't forget the v-bind:key, or simply :key, to the v-for's child elements.
You attempt to attach the <v-menu/> to its default slot. The attach prop of <v-menu/> should be somewhere outside the <v-menu/> or inside the its activator slot. However, it should attach directly to the element in your activator slot by default so you can just omit the attach prop.
<v-menu offset-y> <!-- remove the `attach` prop -->
<template v-slot:activator="{ on, attrs }">
<div class="dropdown" v-bind="attrs" v-on="on">User</div>
</template>
<v-list>...</v-list>
</v-menu>
On your second sub-list, you mistakenly typed search_sub_user instead of search_sub_users:
v-for="(search_sub_users, index) in search_user.sub_users"` // wrong
v-for="(search_sub_user, index) in search_user.sub_users"` // right
Here is a refactored version of your code at codesandbox.

Vuetify Navigation Drawer with Sub-Menus

Like the title says, I'm trying to have a navigation drawer that has expandable sub-menus for certain options. Like a "User Profile" main menu option might have a the sub-menus "Update Contact Details" and "Review Registration".
I've tried this a handful of ways, basically coming down to the same two issues. Because each menu options is a list-tile, either the sub-menu gets stacked on the right of it (as in, the entire sub-menu is in the same tile), or the entire list of menu options has these drop down icons, when only a single menu option actually has a sub-menu. Additionally, my second code snippet below also stops you from navigating to any of the main menu links, which is not what is wanted.
Example 1, where the sub-menu is stuck in the same tile as the main menu option.
<div v-for="(link, i) in links" :key="i">
<v-list-tile v-if="!link.subLinks" :to="link.to" :active-class="color" avatar class="v-list-item">
<v-list-tile-action>
<v-icon>{{ link.icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-title v-text="link.text"/>
</v-list-tile>
<div v-else>
<v-list-tile avatar class="v-list-item">
<v-list-tile-action>
<v-icon>{{ link.icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-title v-text="link.text"/>
<v-list-group>
<v-list-tile sub-group v-for="(subLink, j) in link.subLinks" :key="j" :to="subLink.to" :active-class="color" avatar class="v-list-item">
<v-list-tile-title v-text="subLink.text"/>
</v-list-tile>
</v-list-group>
</v-list-tile>
</div>
</div>
Example 2, where each menu option has a drop down arrow, even ones that don't have any sub-menus.
<v-list-group v-for="(link, i) in links" :key="i" :prepend-icon="link.icon" :to="link.to" :active-class="color" avatar class="v-list-item">
<template v-slot:activator>
<v-list-tile>
<v-list-tile-title>{{ link.text }}</v-list-tile-title>
</v-list-tile>
</template>
<v-list-tile v-for="(subLink, j) in link.subLinks" :key="j" :to="subLink.to" :active-class="color">
<v-list-tile-content>
<v-list-tile-title>{{ subLink.text }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list-group>
This is a sample of the data I'm using
links: [
{
to: '/',
icon: 'mdi-view-dashboard',
text: 'Dashboard',
},
{
icon: 'mdi-account',
text: 'User Profile',
subLinks: [
{
to: '/update-contact',
text: 'Update Contact Details',
},
{
to: '/review-registration',
text: 'Review Registration',
},
],
},
],
What I'd like to be able to do is have a main menu, with the option of adding sub-menus as I see fit. Unfortunately, I can't seem to figure out how to mix and match the list-group and list-tile to get what I want done. I'm super grateful for any help provided. Thanks.
I was looking to do the same thing, here's how I solved it.
Data:
links: [
{
to : '/dashboard',
icon : 'mdi-view-dashboard',
text : 'Dashboard',
},
{
icon : 'mdi-tennis',
text : 'Players',
subLinks : [
{
text : 'Players list',
to : '/players',
},
{
text : 'Import WTA Players',
to : '/players/import',
},
]
},
{
to : '/tournaments',
icon : 'mdi-trophy',
text : 'Tournaments',
},
]
Template:
<v-list>
<div v-for="(link, i) in links">
<v-list-tile
v-if="!link.subLinks"
:key="i"
:to="link.to"
:active-class="color"
avatar
class="v-list-item"
>
<v-list-tile-action>
<v-icon>{{ link.icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-title v-text="link.text" />
</v-list-tile>
<v-list-group
v-else
:key="link.text"
no-action
>
<template v-slot:activator>
<v-list-tile>
<v-list-tile-content>
<v-list-tile-title>{{ link.text }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</template>
<v-list-tile
v-for="sublink in link.subLinks"
:to="sublink.to"
:key="sublink.text"
>
<v-list-tile-title v-text="sublink.text" />
</v-list-tile>
</v-list-group>
</div>
</v-list>
I'm sorry but I don't have time to make a pen. Hope this helps !
I don't have enough reputation to add a comment but this will give you a bit of a better layout and function correctly (in the one posted above links didn't work for some reason and the naming was a bit off)
<template>
<v-navigation-drawer
app
clipped
permanent
mini-variant
expand-on-hover>
<!-- -->
<v-list nav dense>
<div v-for="(link, i) in links" :key="i">
<v-list-item
v-if="!link.subLinks"
:to="link.to"
:active-class="color"
avatar
class="v-list-item"
>
<v-list-item-icon>
<v-icon>{{ link.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-title v-text="link.text" />
</v-list-item>
<v-list-group
v-else
:key="link.text"
no-action
:prepend-icon="link.icon"
:value="false"
>
<template v-slot:activator>
<v-list-item-title>{{ link.text }}</v-list-item-title>
</template>
<v-list-item
v-for="sublink in link.subLinks"
:to="sublink.to"
:key="sublink.text"
>
<v-list-item-icon>
<v-icon>{{ sublink.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ sublink.text }}</v-list-item-title>
</v-list-item>
</v-list-group>
</div>
</v-list>
</v-navigation-drawer>
</template>
<script>
export default {
data: () => ({
links: [
{
to : '/dashboard',
icon : 'mdi-view-dashboard',
text : 'Dashboard',
},
{
icon : 'mdi-folder',
text : 'Templates',
subLinks : [
{
text : 'View Templates',
to : '/templates',
icon : 'mdi-view-list'
},
{
text : 'New Template',
to : '/templates/new',
icon : 'mdi-plus'
},
]
},
{
icon : 'mdi-application',
text : 'Applications',
subLinks : [
{
text : 'View Applications',
to : '/apps',
icon : 'mdi-view-list'
},
{
text : 'New Application',
to : '/apps',
icon : 'mdi-plus'
},
]
},
]
})
}
</script>
<style scoped>
.v-application--is-ltr .v-list--dense.v-list--nav .v-list-group--no-action > .v-list-group__items > .v-list-item {
padding: 0 8px;
}
</style>
Hope this can help.
Basically, the menu on the navigation drawer component (v-navigation-drawer) using the list component (v-list).
From the documentation, you can find a way to add submenu on list component
on the part of nested list
Cheers,
This is three level category list item with navigation-drawer. This is actually find vuetify list section. then i modified this with three level item. You can extend and also optimized code. Just for example I write raw code.
<v-navigation-drawer
v-model="drawer"
:clipped="clipped"
fixed
app
>
<v-list nav dense>
<v-list-item
to="/"
>
<v-list-item-icon>
<v-icon>mdi-home</v-icon>
</v-list-item-icon>
<v-list-item-title>Home</v-list-item-title>
</v-list-item>
<!--Main category list-->
<v-list-group
v-for="item in items"
:value="true"
prepend-icon="mdi-food-apple"
no-action
>
<template v-slot:activator>
<v-list-item-title>{{item.name}}</v-list-item-title>
</template>
<!--Sub category item-->
<!--if 2nd lvl child available-->
<v-list-group
v-if="subItem.children.length > 0"
v-for="subItem in item.children"
:value="true"
sub-group
>
<template v-slot:activator>
<v-list-item-content>
<v-list-item-title>{{subItem.name}}</v-list-item-title>
</v-list-item-content>
</template>
<!--subsubitem category list-->
<v-list-item v-for="subSubItem in subItem.children"
:to="'/category/'+subSubItem.slug">
<v-list-item-icon>
<v-icon></v-icon>
</v-list-item-icon>
<v-list-item-title>{{subSubItem.name}}</v-list-item-title>
</v-list-item>
</v-list-group>
<!--if not 2nd lvl child available-->
<v-list-item :to="'/category/'+subItem.slug" v-for="subItem in
item.children">
<v-list-item-icon>
<v-icon></v-icon>
</v-list-item-icon>
<v-list-item-title>{{subItem.name}}</v-list-item-title>
</v-list-item>
</v-list-group>
</v-list>
</v-navigation-drawer>
DATA:
[
{
"id": 1,
"name": "Food",
"slug": "food",
"children": [
{
"id": 2,
"name": "Fruits & Vegetables",
"slug": "fruits-vegetables",
"children": [
{
"id": 3,
"name": "Fresh Fruits",
"slug": "fresh-fruit",
"children": []
},
{
"id": 4,
"name": "Fresh Vegetables\r\n",
"slug": "fresh-vegetable",
"children": []
}
]
},
{
"id": 5,
"name": "Breakfast",
"slug": "breakfast",
"children": [
{
"id": 6,
"name": "Local Breakfast",
"slug": "local-breakfast",
"children": []
}
]
}
]
},
{
"id": 7,
"name": "Home & Cleaning",
"slug": "home-cleaning",
"children": [
{
"id": 8,
"name": "Air Fresheners",
"slug": "air-freshners",
"children": []
},
{
"id": 9,
"name": "Cleaning Supplies",
"slug": "cleaning-supplies",
}
]
}
]
I just found a way to set the submenu active-class. Hope it can help others.
thanks to VueJS-Linusborg.
<template>
......
<v-list-group
v-else
:key="link.text"
no-action
:prepend-icon="link.icon"
:value="subIsActive('/parentroute')"
>
<template v-slot:activator>
<v-list-item-title>{{ link.text}}</v-list-item-title>
</template>
<v-list-item
v-for="sublink in link.subLinks"
:to="sublink.to"
:key="sublink.text"
:active-class="`success white--text`"
>
<v-list-item-icon>
<v-icon>{{ sublink.icon }}</v-icon>
</v-list-item-icon>
<-list-item-title>{{ sublink.text}}</v-list-item-title>
</v-list-item>
</v-list-group>
...
</template>
<script>
...
methods:{
subIsActive(input) {
const paths = Array.isArray(input) ? input : [input];
return paths.some((path) => {
return this.$route.path.indexOf(path) === 0; // current path starts with this path
string
});
},
....
}</script>