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">
<a href="#!" class="body-2 black--text">EDIT</a>
</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://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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With