Vuetify navigation drawer with multiple submenus - vue.js

I'm trying to get the vuetify navigation menu to work with submenus and I am able to get it to render with v-list but it doesn't work correctly. I want the submenu to look and behave like the top level. How should this html be structured?
<v-navigation-drawer permanent>
<v-list dense nav>
<v-list-item>
<v-list-item-title class="text-h6">Application</v-list-item-title>
</v-list-item>
<v-divider></v-divider>
<v-list-group no-action v-for="item in items" :key="item.id">
<template v-slot:activator>
<v-list-item-content>
<v-list-item-title>
{{ item.name }}
</v-list-item-title>
</v-list-item-content>
</template>
<v-list-item-content v-for="subitem in item.subitems" :key="subitem.id">
<v-list-item-title>
{{ subitem.name }}
<v-list-group no-action sub-group>
<v-list-item-title
v-for="subsubitem in subitem.subsubitems"
:key="subsubitem.id"
>
{{ subsubitem.name }}
</v-list-item-title>
</v-list-group>
</v-list-item-title>
</v-list-item-content>
</v-list-group>
</v-list>
</v-navigation-drawer>
And the data looks something like this:
const items = [
{
"id": 1,
"name": "test1",
"subitems": [
{
"id": 2,
"name": "test2"
"subsubitems": [
{
"id": 3,
"name": "test3",
},
{
"id": 4,
"name": "test4",
},
{
"id": 5,
"name": "test5",
}
]
}
]
}
]

Related

How do I get input from a v-menu?

I am new to Vue, and I am trying to figure out it it all works together. I want to create a drop-down menu so the user can select what type of user they are.
This is my template:
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn color="blue" v-bind="attrs" v-on="on"> User Type </v-btn>
</template>
<v-list>
<v-list-item v-for="(item, index) in items" :key="index" v-model="user_type">
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
And this is my script:
data() {
return {
email: "",
password: "",
error: "",
first_name: "",
surname: "",
items: [{ title: "Client" }, { title: "Freelancer" }],
user_type: "",
};
}
Basically, I want the user to select whether they are a Client or a Freelancer, and I want to store that result in the user_type variable. I was able to store their other details using v-model's like so:
<div class="first_name">
<input
type="first_name"
v-model="first_name"
placeholder="First Name"
/>
</div>
I can't use a v-model with v-menu though, as it seems to return a boolean value for some reason. I have all of the basic functionality working, I just need to be able to access the user's choice in the drop-down menu. Is there something I am just not grasping here?
Thanks!
If you use v-list-item-group tag as parent of v-list-item you can use v-model on v-list-item-group and this will give the index of selected item.
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn color="blue" v-bind="attrs" v-on="on"> User Type </v-btn>
</template>
<v-list>
<v-list-item-group v-model="user_type">
<v-list-item v-for="(item, index) in items" :key="index">
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>

adjust the height of a vuetify item

I am creating a simple table with vuetify and it turns out that when adding the v-radio-group component, the elements in the row are not adjusted to the height, so this causes the table to look ugly.
what property could i use to make the cols all fit to the same height.
I have looked in several places, even in the documentation and I do not see anything like it.
component:
<template>
<v-container>
<v-row class="text-center" no-gutters>
<v-col v-for="(item, index) in headers" v-bind:key="index">
<v-card v-if="item" :key="index" class="pa-2" outlined tile>
{{ item.nombre }}
</v-card>
</v-col>
</v-row>
<div v-for="(item, index) in items" v-bind:key="index">
<v-row no-gutters class="text-center"
align="center"
>
<v-col
align-self="center"
>
<v-card class="pa-2" outlined tile>
{{ item.nombre }}
</v-card>
</v-col>
<v-col>
<v-card class="pa-2" outlined tile>
{{ item.cantidad }}
</v-card>
</v-col>
<v-col offset-md="12">
<v-card outlined tile>
<v-radio-group v-model="radioGroup">
<v-radio
v-for="n in 2"
:key="n"
:label="`Radio ${n}`"
:value="n"
></v-radio>
</v-radio-group>
</v-card>
</v-col>
</v-row>
</div>
</v-container>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
optRadio: [
{ nombre: "Bueno", value: 1},
{ nombre: "Malo", value: 2},
],
headers: [
{ nombre: "Descripcion", value: "descripcion" },
{ nombre: "Cantidad", value: "cantidad" },
{ nombre: "estado", value: "state" },
],
items: [
{ nombre: "RADIO", cantidad: 1, state: 1 },
{ nombre: "FRONTAL", cantidad: 10, state: 0 },
{ nombre: "PARLANTES", cantidad: 100, state: 2 },
{ nombre: "ENCENDEDOR", cantidad: 100, state: 2 },
{ nombre: "CENICERO", cantidad: 100, state: 2 },
{ nombre: "TAPETES", cantidad: 100, state: 2 },
],
};
},
};
</script>
Your key here is applying correct flex attributes to your grid system.
First you'd have to instruct your colums to place content vertically, not horizontally: <v-col class="d-flex flex-column"
Then, instruct the cards within to grow in the available vertical height: <v-card class="pa-2 flex-grow-1 d-flex" outlined tile>. To get the content centered within the card, add these two flex helpers to the card as well: align-center justify-center
Documentation HERE
Codepen solution HERE

Disable v-menu when no item

I've got a vuetify v-menu, with conditionnal item :
<v-menu min-width="225">
<template v-slot:activator="scope">
<v-btn small text color="primary" v-on="scope.on" :loading="actionLoading">
<v-icon>mdi-dots-vertical</v-icon>
</v-btn>
</template>
<v-list dense>
<v-list-item-group color="primary">
<v-list-item v-if="itemOneCondition()" #click="doSomething()">
<v-list-item-content>
<v-list-item-title>
Item 1
</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item v-if="itemTwoCondition()" #click="something()">
<v-list-item-content>
<v-list-item-title>
Title 2
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-list-item-group>
</v-menu>
It happens that something every item is not visible (every v-if conditions are false). So the menu hasn't any item.
In this cas, the button is still activ, and a menu is opened with no entry :
Is there a way to disable the button when no item are visible ?
You need a v-if condition for the entire menu like so
<v-menu v-if="menuitems.length > 0" min-width="225"> </v-menu>
When there are no menu items, or the menu items array.length is 0, then the menu panel will not be displayed
Checkout this code snippet, You need to store the menu items in an array for this to work. In the code snippet below, when you replace
items: ["Item1","Item2",] with items: []
Notice that the menu with the black background will not be displayed
var app = new Vue({
el: "#menu",
data () {
return {
items: [
"Item1",
"Item2",
]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="menu" id="menu">
<ul style="background: black; color: yellow" v-if="items.length > 0">
<li v-for="(item, index) in items" :key="index">{{item}}</li>
</ul>
</div>
I used this solution, by adding a isAtLeastOneItemDisplayed() function :
<v-menu v-if="isAtLeastOneItemDisplayed()" min-width="225">
<template v-slot:activator="scope">
<v-btn small text color="primary" v-on="scope.on" :loading="actionLoading">
<v-icon>mdi-dots-vertical</v-icon>
</v-btn>
</template>
<v-list dense>
<v-list-item-group color="primary">
<v-list-item v-if="itemOneCondition()" #click="doSomething()">
<v-list-item-content>
<v-list-item-title>
Item 1
</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item v-if="itemTwoCondition()" #click="something()">
<v-list-item-content>
<v-list-item-title>
Title 2
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-list-item-group>
</v-menu>
isAtLeastOneItemDisplayed : function(){
return this.itemOneCondition()
|| this.itemTwoCondition();
}

How to "stop" an event?

I'm learning a Vuetifyjs and try writing a "file explorer".
There is an example:
codepen-snippet
I can’t understand how to make sure that when you click on the right icon, the entry in the "tree" does not become "active".
Probably need to “stop” the events, but I don’t know how to do it.
Tell me.
Thank.
I want to click on this menu: Menu
it became like this: need
not like now: now
Snippet:
<div id="app">
<v-app id="inspire">
<v-content>
<v-container >
<v-layout justify-center>
<v-card min-width=400>
<v-treeview
v-model="tree"
:open="open"
:items="items"
activatable
hoverable
item-key="name"
open-on-click
>
<template v-slot:prepend="{ item, open }">
<v-icon v-if="!item.file">
{{ open ? 'mdi-folder-open' : 'mdi-folder' }}
</v-icon>
<v-icon v-else>
{{ files[item.file] }}
</v-icon>
</template>
<template v-slot:label="{item}">
<v-hover v-slot:default="{ hover }">
<div class="d-flex align-center">
<span>{{item.name}}</span>
<v-menu
class="ml-auto"
style="display: inline"
:nudge-width="200"
offset-y
>
<template v-slot:activator="{ on }">
<!--
-->
<v-btn
v-show="hover"
icon
small
v-on="on"
class="pa-0 ma-0"
>
<v-icon small class="pa-0 ma-0">more_vert</v-icon>
</v-btn>
</template>
<v-card>
<v-list>
<v-list-item #click="() => {}">
<v-list-item-action>
<v-icon>mdi-information-variant</v-icon>
</v-list-item-action>
<v-list-item-title>Info</v-list-item-title>
</v-list-item>
<v-list-item v-if="item.type === 'process' || item.type === 'state'" #click="() => {}">
<v-list-item-action>
<v-icon>power_settings_new</v-icon>
</v-list-item-action>
<v-list-item-title>Status</v-list-item-title>
</v-list-item>
<v-list-item #click="() => {}">
<v-list-item-action>
<v-icon>create</v-icon>
</v-list-item-action>
<v-list-item-title>Rename</v-list-item-title>
</v-list-item>
<v-list-item #click="() => {}">
<v-list-item-action>
<v-icon>file_copy</v-icon>
</v-list-item-action>
<v-list-item-title>Copy</v-list-item-title>
</v-list-item>
<v-list-item #click="() => {}">
<v-list-item-action>
<v-icon>mdi-folder-plus</v-icon>
</v-list-item-action>
<v-list-item-title>Create folder</v-list-item-title>
</v-list-item>
<v-list-item #click="() => {}">
<v-list-item-action>
<v-icon>delete</v-icon>
</v-list-item-action>
<v-list-item-title>Delete</v-list-item-title>
</v-list-item>
</v-list>
</v-card>
</v-menu>
</div>
</v-hover>
</template
</v-treeview>
</v-card
</v-layout justify-center>
</v-container>
</v-content>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
open: ['public'],
files: {
html: 'mdi-language-html5',
js: 'mdi-nodejs',
json: 'mdi-json',
md: 'mdi-markdown',
pdf: 'mdi-file-pdf',
png: 'mdi-file-image',
txt: 'mdi-file-document-outline',
xls: 'mdi-file-excel',
},
tree: [],
items: [
{
name: '.git',
},
{
name: 'node_modules',
},
{
name: 'public',
children: [
{
name: 'static',
children: [{
name: 'logo.png',
file: 'png',
}],
},
{
name: 'favicon.ico',
file: 'png',
},
{
name: 'index.html',
file: 'html',
},
],
},
{
name: '.gitignore',
file: 'txt',
},
{
name: 'babel.config.js',
file: 'js',
},
{
name: 'package.json',
file: 'json',
},
{
name: 'README.md',
file: 'md',
},
{
name: 'vue.config.js',
file: 'js',
},
{
name: 'yarn.lock',
file: 'txt',
},
],
}),
})
What is happening here is that your click event is propagating to its parent element, so when you click on the icon to display the menu it also triggers the click event of your parent element which is the file or folder container.
You can add #click.stop to your v-btn in line 44, like this:
<template v-slot:activator="{ on }">
<!--
-->
<v-btn
v-show="hover"
icon
small
v-on="on"
class="pa-0 ma-0"
#click.stop
>
<v-icon small class="pa-0 ma-0">more_vert</v-icon>
</v-btn>
</template>
This will stop the event from propagating to its parent element, you can try it out here: codepen-snippet
Now when you click the button it will display your menu and won't change the active or inactive state on your files or folders.
To remove the blue highlighting of items when clicked, remove "activatable" from the v-treeview component:
<v-treeview
v-model="tree"
:open="open"
:items="items"
activatable
hoverable
item-key="name"
open-on-click >

router-link with vue and vuetify confusion

How to use vue-router with using that predefined template:
https://vuetifyjs.com/examples/layouts/google-contacts
I have added a link in my items object
items:
[{ icon: 'dashboard' text: 'Home', link: '/'},
{ icon: 'dashboard' text: 'Account', link: '/account'},
I am confused where to put the router-link component.
v-list-tile, v-btn, and v-card all extend router-link, so you can use any of the router-link attributes directly on those components instead.
In your case you can just use <v-list-tile :to="item.link">
I had the same problem, and I solved it like this:
<v-list-item v-else :key="item.text" link>
<!-- to -->
<v-list-item v-else :key="item.text" :to="item.link" link>
<v-list-item v-for="(child, i) in item.children" :key="i" link>
<!-- to -->
<v-list-item v-for="(child, i) in item.children" :key="i" :to="child.link" link>
JS
{ icon: "mdi-history", text: "Recientes", link: "/" },
Don't forget to put <router-view /> in the container.
<v-content>
<v-container class="fill-height" fluid>
<router-view />
</v-container>
</v-content>
An example:
<template>
<v-navigation-drawer
app
clipped
permanent
expand-on-hover
>
<v-list>
<v-list-item class="px-2">
<v-list-item-avatar>
<v-img src="https://randomuser.me/api/portraits/women/85.jpg"></v-img>
</v-list-item-avatar>
</v-list-item>
<v-list-item link>
<v-list-item-content>
<v-list-item-title class="title">Sandra Adams</v-list-item-title>
<v-list-item-subtitle>sandra_a88#gmail.com</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list>
<v-divider></v-divider>
<v-list
nav
dense
>
<v-list-item v-for="(item, i) in items" :key="i" :to="item.link" link>
<v-list-item-icon>
<v-icon>{{item.icon}}</v-icon>
</v-list-item-icon>
<v-list-item-title>{{item.text}}</v-list-item-title>
</v-list-item>
</v-list>
</v-navigation-drawer>
</template>
<script>
export default {
name: 'MenuDrawer',
data: () => ({
items: [
{
icon: 'mdi-folder',
text: 'Home',
link: '/',
},
{
icon: 'mdi-account-multiple',
text: 'Starred',
link: 'https://github.com/vuetifyjs/vuetify',
},
{
icon: 'mdi-star',
text: 'About',
link: '/about',
},
],
}),
};
</script>
I had a similar task to achieve and I was able to do it in the following way.
See the following code below.
<v-list-item v-for="([icon, text, link], i) in items"
:key="i"
link
#click="$vuetify.goTo(link)" >
<v-list-item-icon class="justify-center">
<v-icon>{{ icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title class="subtitile-1">{{
text
}}</v-list-item-title>
</v-list-item-content>
</v-list-item>
As per the documentation as we specify the link prop in the item it will be considered as an href to perform navigation.
Then you can make use of the click function and pass the parameter link inside it.