Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 948
User_4373 Avatar asked Apr 27 '18 14:04

User_4373


2 Answers

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

like image 105
Alex Gogl Avatar answered Sep 20 '22 05:09

Alex Gogl


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>
like image 29
user3507486 Avatar answered Sep 19 '22 05:09

user3507486