Combining function clicks with router paths in a Vue Drawer using a v-list - vue.js

I have a
<v-navigation-drawer>
with the following code:
<v-list-tile
v-for="(child, i) in item.children"
:key="i"
v-on:click.stop="goPage(child.link)"
#click="{{child.link}}"
>
<v-list-tile-action v-if="child.icon">
<v-icon>{{ child.icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>{{child.text }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list-group>
<v-list-tile
v-else
:key="item.text"
v-on:click.stop="goPage(item.link)"
#click="{{item.link}}"
>
<v-list-tile-action>
<v-icon>{{ item.icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>{{ item.text }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</template>
</v-list>
And my menu_itemsArray is
menu_items: [
{
icon: "keyboard_arrow_up",
"icon-alt": "keyboard_arrow_down",
text: "Members",
model: false,
children: [{ icon: "contacts", text: "Membership", link: "/members/"
},
{
icon: "library_books",
text: "Report Workspace",
link: "/publications/workspace/"
},
icon: "library_books",
text: "Load Data",
Call function
}]
},
It works fine calling various links to load Vue pages using the Router. My question is how can I make one of the menu items just call a function and stay on the same page?
Thanks

Yes Let's say you need to add logout button as well. Then you can add it in the array as the following.
methods: {
logout () {
console.log('Logout clicked!');
},
goPage (ref) {
this.$router.push({ path: ref });
},
},
data() {
return {
menu_items: [{
icon: "keyboard_arrow_up",
"icon-alt": "keyboard_arrow_down",
text: "Members",
model: false,
children: [
{ icon: "contacts", text: "Membership", type: 'link', ref: "/page2/" },
{ icon: "input", text: "Sign out", type: 'button', func: this.logout },
],
}]
}
}
Then v-navigation-drawer should look like the following:
<v-navigation-drawer app fixed clipped>
<v-list v-for="(items, index) in menu_items" :key="index">
<v-list-tile
v-for="(child, i) in items.children"
:key="i"
#click.stop="child.type === 'link' ? goPage(child.ref) : child.func.apply(this)">
<v-list-tile-action v-if="child.icon">
<v-icon>{{ child.icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>{{child.text }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
</v-navigation-drawer>

Related

Vuejs sidebar menu with subitems

I am trying to create a sidebar menu dinamically with vuejs and vuenotify.
I have seen a sample on vuetify site using something as bellow.
The problem with this code, is that always shows the append-icon ">" on end of item title .
I´d like to show the append-icon ">", only if I have subittems.
I have created an item called "Manuutenção" with subtitems. No other has subitems.
Then, i´d like only "Manutençao" item or other(if I create with subitems") showing the ">".
Is possible fix it?
<v-list>
<v-list-group
v-for="item in items"
:key="item.title"
v-model="item.active"
:prepend-icon="item.icon"
no-action
>
<template v-slot:activator>
<v-list-item-content>
<v-list-item-title v-text="item.title"></v-list-item-title>
</v-list-item-content>
</template>
<v-list-item
v-for="subItem in item.items"
:key="subItem.title"
link
router
:to="subItem.to"
>
<v-list-item-content>
<v-list-item-title v-text="subItem.title"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-group>
</v-list>
export default {
name: "DashboardCoreDrawer",
data: () => ({
items: [
{
icon: "mdi-view-dashboard",
title: "Dashboard",
to: "/",
},
{
icon: "mdi-account",
title: "Usuários",
to: "/usuarios",
},
{
title: "Clientes",
icon: "mdi-map-marker",
to: "/clientes",
},
{
title: "Manutenção",
icon: "mdi-clipboard-outline",
to: "",
items: [
{
title: "Convênios",
icon: "mdi-clipboard-outline",
to: "/convenios",
},
{
title: "Planos",
icon: "mdi-format-font",
to: "/planos",
},
],
},
{
title: "Convênios",
icon: "mdi-clipboard-outline",
to: "/convenios",
},
{
title: "Planos",
icon: "mdi-format-font",
to: "/planos",
},
{
title: "Tabelas",
icon: "mdi-chart-bubble",
to: "/tabelas",
},
{
title: "Atendimento",
icon: "mdi-bell",
to: "atendimentos",
},
],
}),
}
Additional information:
Is possible make adaptations to two or three submenu items. For sample:
items:[
{
title: "Manutenção",
icon: "mdi-clipboard-outline",
to: "",
items: [
{
title: "Convênios",
icon: "mdi-clipboard-outline",
to: "/convenios",
},
{
title: "Planos",
icon: "mdi-format-font",
to: "",
items: [
{
title: "Test1,
icon: "mdi-chart-bubble",
to: "/test1",
},
{
title: "Test2",
icon: "mdi-chart-bubble",
to: "/test2",
},
],
},
]
Icon at "Planos"
The main trick is to render v-list-group when item has subitems and v-list-item otherwise. See an example below:
<template>
<v-list>
<template v-for="item in items">
<v-list-group
:key="item.title"
v-if="item.items !== undefined"
v-model="item.active"
no-action
>
<template v-slot:activator>
<v-list-item-avatar left>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title v-text="item.title"></v-list-item-title>
</v-list-item-content>
</template>
<v-list-item
v-for="subItem in item.items"
:key="subItem.title"
link
router
:to="subItem.to"
>
<v-list-item-avatar left>
<v-icon>{{ subItem.icon }}</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title v-text="subItem.title"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-group>
<v-list-item v-else :key="item.title" link router :to="item.to">
<v-list-item-avatar left>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title v-text="item.title"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</template>
</v-list>
</template>
Edited:
For a multilevel solution, basically, you have to create a component for list item and import it recursively into itself. Something like that:
mainlist.vue
<template>
<div>
<v-list>
<list-item v-for="item in items" :item="item" :key="item.title">
</list-item>
</v-list>
</div>
</template>
<script>
export default {
name: "DashboardCoreDrawer",
components: {
ListItem: () => import("./listitem.vue"),
},
data: () => ({
items: [
{
title: "Convênios",
icon: "mdi-clipboard-outline",
to: "/convenios",
},
{
title: "Planos",
icon: "mdi-format-font",
items: [
{
title: "Test1",
icon: "mdi-chart-bubble",
items: [
{
title: "Test4",
icon: "mdi-chart-bubble",
to: "/test1",
},
{
title: "Test5",
icon: "mdi-chart-bubble",
to: "/test2",
},
],
},
{
title: "Test2",
icon: "mdi-chart-bubble",
to: "/test2",
},
],
},
],
}),
};
</script>
listitem.vue
<template>
<div>
<v-list-group
v-if="item.items !== undefined"
v-model="item.active"
no-action
:sub-group="isSubGroup"
:class="isSubGroup ? 'right-icon' : ''"
>
<template v-slot:activator>
<v-list-item-content>
<v-list-item-title>
<v-icon>{{ item.icon }}</v-icon>{{ item.title }}
</v-list-item-title>
</v-list-item-content>
</template>
<template v-for="subItem in item.items">
<list-item :item="subItem" :is-sub-group="true" :key="subItem.title">
</list-item>
</template>
</v-list-group>
<v-list-item v-else link router :to="item.to">
<v-list-item-avatar left>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title v-text="item.title"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</div>
</template>
<script>
export default {
name: "listitem",
props: ["item", "isSubGroup"],
components: {
ListItem: () => import("./listitem.vue"),
},
};
</script>
<style>
.right-icon .v-list-group__header {
display: flex !important;
flex-direction: row-reverse !important;
}
</style>

Vue stop list/expansion

I would like to stop a list from collapsing if I click on the expanded element. So if I click on any collapsed header it should expand, but if I click on the same header it shouldn't collapse but remain active/expanded (clicking on some other collapsed header will work and the previous will collapse). I've tried fiddling with v-model, :value, and stopPropagation() without any luck. Here is a codepen where the active prop is correct but the elements still collapse when active.
new Vue({
el: '#app',
data () {
return {
disableNavbar: true,
items: [
{
action: 'local_activity',
title: 'Attractions',
items: [
{ title: 'List Item' }
]
},
{
action: 'restaurant',
title: 'Dining',
active: true,
items: [
{ title: 'Breakfast & brunch' },
{ title: 'New American' },
{ title: 'Sushi' }
]
},
{
action: 'school',
title: 'Education',
items: [
{ title: 'List Item' }
]
},
{
action: 'directions_run',
title: 'Family',
items: [
{ title: 'List Item' }
]
},
{
action: 'healing',
title: 'Health',
items: [
{ title: 'List Item' }
]
},
{
action: 'content_cut',
title: 'Office',
items: [
{ title: 'List Item' }
]
},
{
action: 'local_offer',
title: 'Promotions',
items: [
{ title: 'List Item' }
]
}
]
}
},
methods: {
test(i,e) {
console.log('clicked'+i+' '+JSON.stringify(e));
if(this.items[i].hasOwnProperty('active')){
console.log(this.items[i].active.toString());
}
this.items.forEach((obj,ind)=>{
if(ind!==i){
obj.active=false;
}
})
console.log(JSON.stringify(this.items));
if(this.items[i].hasOwnProperty('active') && this.items[i].active){
event.stopPropagation();
e.stopPropagation();
}
this.items[i].active=true;
}
}
})
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<v-card>
<v-list readonly>
<v-list-group
v-for="(item, i) in items"
:key="item.title"
:value="item.active"
:prepend-icon="item.action"
no-action
#click.prevent="test(i,$event)"
>
<template v-slot:activator>
<v-list-tile>
<v-list-tile-content>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</template>
<v-list-tile
v-for="subItem in item.items"
:key="subItem.title"
#click=""
>
<v-list-tile-content>
<v-list-tile-title>{{ subItem.title }}</v-list-tile-title>
</v-list-tile-content>
<v-list-tile-action>
<v-icon>{{ subItem.action }}</v-icon>
</v-list-tile-action>
</v-list-tile>
</v-list-group>
</v-list>
</v-card>
</v-flex>
</v-layout>
Simple solution: #click.native.stop="yourAction".

List groupe inside dialog renders title out of correct position

List group component renders data incorrectly-
the title that should be on the slot activator tile is being rendered along with the drop-down elements.
I used an example of vuetify.js API here:
https://vuetifyjs.com/en/components/lists.
even with the original example, i get the same results.
Outer component:
<template>
<v-container>
<v-dialog :v-model="dialog" persistent width="800">
<v-card>
<v-card-title class="headline grey lighten-4" primary-title>File Results
<v-spacer/>
<v-btn flat icon #click="close">
<v-icon color="black" medium>cancel</v-icon>
</v-btn>
</v-card-title>
<ResultListView/>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="primary" flat #click="openFileDialog">results</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-container>
</template>
List component out of vuetify exapmle:
<template>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<v-card>
<v-toolbar color="teal" dark>
<v-toolbar-side-icon></v-toolbar-side-icon>
<v-toolbar-title>Topics</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon>
<v-icon>more_vert</v-icon>
</v-btn>
</v-toolbar>
<v-list>
<v-list-group
v-for="item in items"
:key="item.title"
v-model="item.active"
:prepend-icon="item.action"
aria-expanded="true"
no-action
>
<template v-slot:activator>
<v-list-tile>
<v-list-tile-content>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</template>
<v-list-tile
v-for="subItem in item.items"
:key="subItem.title"
>
<v-list-tile-content>
<v-list-tile-title>{{ subItem.title }}</v-list-tile-title>
</v-list-tile-content>
<v-list-tile-action>
<v-icon>{{ subItem.action }}</v-icon>
</v-list-tile-action>
</v-list-tile>
</v-list-group>
</v-list>
</v-card>
</v-flex>
</v-layout>
</template>
<script>
export default {
data () {
return {
items: [
{
action: 'local_activity',
title: 'Attractions',
items: [
{ title: 'List Item' }
]
},
{
action: 'restaurant',
title: 'Dining',
active: true,
items: [
{ title: 'Breakfast & brunch' },
{ title: 'New American' },
{ title: 'Sushi' }
]
},
{
action: 'school',
title: 'Education',
items: [
{ title: 'List Item' }
]
},
{
action: 'directions_run',
title: 'Family',
items: [
{ title: 'List Item' }
]
},
{
action: 'healing',
title: 'Health',
items: [
{ title: 'List Item' }
]
},
{
action: 'content_cut',
title: 'Office',
items: [
{ title: 'List Item' }
]
},
{
action: 'local_offer',
title: 'Promotions',
items: [
{ title: 'List Item' }
]
}
]
}
}
}
</script>
I expect the results to be similar to the supplied API example
but I get the following result:
Since you use version 1.4.1, you should also look at the correct version's documentation of vuetify,
If you want to do any changes just go through this documentation for v1 https://v1.vuetifyjs.com/en/components/lists#list, Or else just update vuetify version.
the below is the code for your solution,
<template>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<v-card>
<v-toolbar color="teal" dark>
<v-toolbar-side-icon></v-toolbar-side-icon>
<v-toolbar-title>Topics</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon>
<v-icon>more_vert</v-icon>
</v-btn>
</v-toolbar>
<v-list>
<v-list-group
v-for="item in items"
v-model="item.active"
:key="item.title"
:prepend-icon="item.action"
no-action
>
<v-list-tile slot="activator">
<v-list-tile-content>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-list-tile v-for="subItem in item.items" :key="subItem.title">
<v-list-tile-content>
<v-list-tile-title>{{ subItem.title }}</v-list-tile-title>
</v-list-tile-content>
<v-list-tile-action>
<v-icon>{{ subItem.action }}</v-icon>
</v-list-tile-action>
</v-list-tile>
</v-list-group>
</v-list>
</v-card>
</v-flex>
</v-layout>
</template>
<script>
export default {
data () {
return {
items: [
{
action: 'local_activity',
title: 'Attractions',
items: [
{ title: 'List Item' }
]
},
{
action: 'restaurant',
title: 'Dining',
active: true,
items: [
{ title: 'Breakfast & brunch' },
{ title: 'New American' },
{ title: 'Sushi' }
]
},
{
action: 'school',
title: 'Education',
items: [
{ title: 'List Item' }
]
},
{
action: 'directions_run',
title: 'Family',
items: [
{ title: 'List Item' }
]
},
{
action: 'healing',
title: 'Health',
items: [
{ title: 'List Item' }
]
},
{
action: 'content_cut',
title: 'Office',
items: [
{ title: 'List Item' }
]
},
{
action: 'local_offer',
title: 'Promotions',
items: [
{ title: 'List Item' }
]
}
]
}
}
}
</script>
Hope it helps !

Parent Vuetify VMenu Not Closing When Child Selected

My app creates it's main navigation dynamically via for loops to generated nested vmenu's and vlists. It generates correctly. The problem is when a user clicks a link in a nested menu, the parent menu remains open. I've tried close-on-click and close-on-content-click on both the main and nested menu, but that's not working.
My codepen has a simplified example. If you select MENU 1, then hover over Subitem 1-1 and click on Subitem 1-1-1, that immediate menu closes, but the main menu remains open.
My codepen.
<div id="app">
<v-app id="inspire">
<v-container>
<!-- top level menu -->
<v-menu
v-for="(menu,index) in menus"
:key="`menu-${index}`"
offset-y
close-on-click
#click="route()"
>
<v-btn
flat
primary
slot="activator"
>{{ menu.title }}</v-btn>
<!-- each item on a menu .. is a menuitem -->
<v-list dense>
<v-list-tile
v-for="(menuitem,index) in menu.items"
:key="`menuitem-${index}`"
#click="route()"
>
<!-- or a popout of submenu items -->
<v-list-tile-content>
<v-menu
offset-x
open-on-hover
close-on-click
>
<v-list-tile
slot="activator"
#click="route()"
>
<v-list-tile-title>{{ menuitem.title }}</v-list-tile-title>
<v-list-tile-action
class="justify-end"
v-if="menuitem.items"
>
<v-icon primary>arrow_right</v-icon>
</v-list-tile-action>
</v-list-tile>
<v-list
dense
v-if="menuitem.items"
>
<v-list-tile
v-for="(menusubitem,index) in menuitem.items"
:key="`menusubitem-${index}`"
#click="route()"
>
<v-list-tile-title>{{ menusubitem.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</v-list-tile-content>
</v-list-tile>
</v-list>
</v-menu>
</v-container>
</v-app>
</div>
And the related script.
new Vue({
el: '#app',
data: () => ({
menus: [
{
title: 'MENU 1',
items: [
{ title: 'Subitem 1-1',
items: [
{ title: 'Subitem 1-1-1' },
{ title: 'Subitem 1-1-2' }
]
},
{ title: 'Subitem 1-2' },
{ title: 'Subitem 1-3' }
]
},
{
title: 'MENU 2',
},
{
title: 'MENU 3',
items: [
{ title: 'Subitem 3-1' },
{ title: 'Subitem 3-2' },
]
}]
}),
methods: {
route() {
console.log("replace with router")
}
}
})
One option would be to access and close parent menu explicitly via isActive property:
route(parentMenuIndex) {
if (arguments.length) {
const parentMenu = this.$refs.menuRef[parentMenuIndex];
parentMenu.isActive = false;
}
}
where this.$refs.menuRef is used to access parent menu component via the ref attribute:
<v-menu
v-for="(menu,index) in menus"
:key="`menu-${index}`"
offset-y
close-on-click
close-on-content-click
ref="menuRef"
>
...
</v-menu>
Here is an updated CodePen

How to use vuetify component v-navigation-drawer, toolbar, footer separately in different files

I am trying to implement the vuetify in my project. I wanted to separate the
<v-navigation-drawer>, </v-toolbar> and <v-footer> in three different files.
Currently i am using.
Layout.vue
<template>
<v-app>
<TopNav :drawer="drawer" :clipped="clipped"></TopNav>
<SideBar/>
<v-content>
<v-container fluid>
<router-view></router-view>
</v-container>
</v-content>
<FooterArea/>
</v-app>
</template>
Script- Layout.vue
<script>
import { TopNav, FooterArea, SideBar } from "../layouts/index";
export default {
name: "Full",
components: {
TopNav,
FooterArea,
SideBar
},
data() {
return {
clipped: true,
drawer: true,
fixed: false,
inset: true,
items: [
{
icon: "bubble_chart",
title: "Inspire"
}
],
miniVariant: false,
right: true,
rightDrawer: false,
title: "Vuetify.js"
};
}
};
</script>
TopNav.vue
<template>
<v-toolbar dark color="info" app :clipped-left="clipped">
<v-toolbar-side-icon v-model="drawer" #click.stop="drawer = !drawer"></v-toolbar-side-icon>
<v-toolbar-title class="white--text">Title</v-toolbar-title>
</v-toolbar>
</template>
<script>
export default {
props: {
clipped: {
type: Boolean,
default: true
},
drawer: {
type: Boolean,
default: true
}
}
};
</script>
SideBar.vue
<template>
<v-navigation-drawer
persistent
:mini-variant="miniVariant"
:clipped="clipped"
v-model="drawer"
enable-resize-watcher
fixed
app
>
<v-list>
<v-list-tile
value="true"
v-for="(item, i) in items"
:key="i"
>
<v-list-tile-action>
<v-icon v-html="item.icon"></v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title v-text="item.title"></v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
</v-navigation-drawer>
</template>
<script>
export default {
}
</script>
However have tried by using the props and passing the values from Layout.vue to TopNav.vue, but i am getting the error as:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "drawer"
As I need to emit from the TopNav.vue to Layout.vue but i couldnot understand how it can be done nicely.
Thank you in advance for the help.
I use it like this:
Parent
<template>
<v-app id="inspire">
<TheNavDrawer :items="items" v-model="drawer" />
<v-toolbar
:clipped-left="$vuetify.breakpoint.lgAndUp"
color="orange darken-3"
dark
app
fixed
>
<v-toolbar-side-icon
#click.stop="drawer = !drawer"
class="hidden-xs-only"
/>
<v-toolbar-title>Test App</v-toolbar-title>
</v-toolbar>
</v-app>
</template>
<script>
import TheNavDrawer from "#/components/Navigation/TheNavDrawer";
export default {
data: () => ({
drawer: false,
items: [
{ icon: "contacts", text: "Contacts" },
{ icon: "history", text: "Frequently contacted" },
{ icon: "content_copy", text: "Duplicates" },
{
icon: "keyboard_arrow_up",
"icon-alt": "keyboard_arrow_down",
text: "Labels",
model: true,
children: [{ icon: "add", text: "Create label" }]
},
{
icon: "keyboard_arrow_up",
"icon-alt": "keyboard_arrow_down",
text: "More",
model: false,
children: [
{ text: "Import" },
{ text: "Export" },
{ text: "Print" },
{ text: "Undo changes" },
{ text: "Other contacts" }
]
},
{ icon: "settings", text: "Settings" },
{ icon: "chat_bubble", text: "Send feedback" },
{ icon: "help", text: "Help" },
{ icon: "phonelink", text: "App downloads" },
{ icon: "keyboard", text: "Go to the old version" }
]
}),
components: {
TheNavDrawer,
}
};
</script>
TheNavDrawer.vue
<template>
<v-navigation-drawer
v-bind:value="value" # <-- mimic v-model behaviour
v-on:input="$emit('input', $event)" <-- mimic v-model behaviour
:clipped="$vuetify.breakpoint.lgAndUp"
fixed
app
>
<v-list dense>
<template v-for="item in items">
<v-layout v-if="item.heading" :key="item.heading" row align-center>
<v-flex xs6>
<v-subheader v-if="item.heading">
{{ item.heading }}
</v-subheader>
</v-flex>
<v-flex xs6 class="text-xs-center">
EDIT
</v-flex>
</v-layout>
<v-list-group
v-else-if="item.children"
:key="item.text"
v-model="item.model"
:prepend-icon="item.model ? item.icon : item['icon-alt']"
append-icon=""
>
<v-list-tile slot="activator">
<v-list-tile-content>
<v-list-tile-title>
{{ item.text }}
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-list-tile
v-for="(child, i) in item.children"
:key="i"
#click="false"
>
<v-list-tile-action v-if="child.icon">
<v-icon>{{ child.icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>
{{ child.text }}
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list-group>
<v-list-tile v-else :key="item.text" #click="false">
<v-list-tile-action>
<v-icon>{{ item.icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>
{{ item.text }}
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</template>
</v-list>
</v-navigation-drawer>
</template>
<script>
export default {
props: {
items: {
type: Array,
required: true
},
value: {
type: Boolean,
default: false
}
}
};
</script>
Basically, instead of using v-model inside the NavDrawer child component I define my own v-model with v-bind:value="value" and v-on:input="$emit('input', $event)" which propagates the status from the <v-navigation-drawer> up to the parent component and makes for much cleaner code. If you want to know more about what happens behind the scenes look here: https://v2.vuejs.org/v2/guide/components-custom-events.html
This is how I use navigation drawer as Parent/Child components without using vuex state management.
Parent Compontent
<Drawer :items="navigations.user.items" :drawer="navigations.user.drawer" #drawerStatus="navigations.user.drawer = $event" :position="navigations.user.position" :avatar="navigations.user.avatar" />
<v-toolbar color="transparent" flat dark absolute>
<v-toolbar-side-icon #click.native.stop="navigations.default.drawer = !navigations.default.drawer">
</v-toolbar-side-icon>
<v-toolbar-title>Open Voucher</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon>
<v-icon #click.native.stop="navigations.user.drawer = !navigations.user.drawer">more_vert</v-icon>
</v-btn>
</v-toolbar>
</div>
</template>
<script>
import Drawer from './Drawer'
export default {
components: {
Drawer
},
data () {
return {
drawer: null,
navigations:
{
default: {
drawer: false,
position: null,
avatar: false,
items: [
{ title: 'Item title', icon: 'fas fa-home', url: '/' },
{ title: 'Item title', icon: 'fas fa-user-friends', url: '/item-url' },
{ title: 'Item title', icon: 'fas fa-atlas', url: '/item-url' },
{ title: 'Item title', icon: 'fas fa-archway', url: '/item-url' },
{ title: 'Item title', icon: 'fas fa-pencil-alt', url: '/item-url' }
]
},
user: {
drawer: false,
position: 'right',
avatar: true,
items: [
{ title: 'Item title', icon: 'dashboard', url: '/item-url' },
{ title: 'Item title', icon: 'fas fa-map-marked-alt', url: '/item-url' },
{ title: 'Item title', icon: 'fas fa-heart', url: '/item-url' },
{ title: 'Item title', icon: 'question_answer', url: '/item-url' }
]
}
}
}
},
methods: {
changeDrawerStatus(value) {
this.drawer = value;
}
}
}
</script>
Child Component
<template>
<v-navigation-drawer v-model="drawerChild" fixed temporary app :right="position">
<v-list class="pa-1" v-if="avatar">
<v-list-tile avatar>
<v-list-tile-avatar>
<img src="https://randomuser.me/api/portraits/men/85.jpg">
</v-list-tile-avatar>
<v-list-tile-content>
<v-list-tile-title>John Leider</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
<v-list class="pt-0" dense>
<v-divider></v-divider>
<v-list-tile v-for="item in itemList" :key="item.title" :to="item.url">
<v-list-tile-action>
<v-icon>{{ item.icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
</v-navigation-drawer>
</template>
<script>
export default {
props: [
'items',
'drawer',
'position',
'avatar'
],
data() {
return {
drawerChild: null,
itemList: []
}
},
mounted() {
this.itemList = this.items;
},
watch: {
drawer (value) {
this.drawerChild = value;
},
drawerChild (value) {
this.$emit('drawerStatus', value)
}
}
}
</script>