Trying to bind props to v-model

I am using vuetify.js and trying to create a component which can be reusable across the application. Although its working absolutely fine, but I am not sure if it's the correct way.

I am creating a navigation drawer component which has the same menu options all the time but it can be opened from UI elements.

Below is the code.

// NavigationBar.vue

<template>
  <v-navigation-drawer
    temporary
    v-model="drawerFlag"
    light
    overflow
    fixed
  >
    <v-list>
      <v-list-tile>
        <v-list-tile-action @click.stop="toggleDrawer()">
          <v-btn icon>
            <v-icon>close</v-icon>
          </v-btn>
        </v-list-tile-action>
      </v-list-tile>
    </v-list>
    <v-list class="pt-0">
      <template v-for="item in items">
        <v-list-tile :key="item.title" :to="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.title }}</v-list-tile-title>
          </v-list-tile-content>
        </v-list-tile>
        <v-divider></v-divider>
      </template>
    </v-list>
  </v-navigation-drawer>
</template>

<script>
  export default {
    props: ['drawer'],
    data() {
      return {
        items: [
          { title: 'Home', icon: 'home', link: '/home'},
          { title: 'History', icon: 'history', link: '/history' },
          { title: 'Wallet', icon: 'account_balance_wallet', link: '/wallet' },
          { title: 'My Profile', icon: 'person', link: '/profile' },
          { title: 'Settings', icon: 'settings', link: '/settings' },
          { title: 'About', icon: 'error', link: '/about' },
          { title: 'Logout', icon: 'power_settings_new', link: '/logout' },
        ]
      };
    },
    computed: {
      drawerFlag: {
        get: function() {
          return this.drawer
        },
        set: function() {

        }
      }
    },
    methods: {
      toggleDrawer: function() {
        this.$emit('emitToggleDrawer');
      }
    }
  }
</script>

//Home.vue

<template>
  <div class="full-screen">
    <navigation-bar :drawer="drawer" v-on:emitToggleDrawer="toggleDrawer"></navigation-bar>
    <v-btn icon class="mt-3 fixed-position" @click.stop="drawer = !drawer">
      <v-icon>menu</v-icon>
    </v-btn>
  </div>
</template>

<script>
  export default {
    name: 'home',
    data() {
      return {
        drawer: null
      };
    },

    computed: {
      user() {
        return this.$store.getters.user;
      }

    },

    methods: {
      toggleDrawer: function () {
        this.drawer = !this.drawer;
      }
    }
  };
</script>

In the above code..

In parent component, I have button to open navigation-drawer and the state of the navigation drawer is maintained in the parent component called "drawer". Then, I am passing "drawer" as a prop to child component and a method to trigger an event from child component to parent component called "emitToggleDrawer".

In child component, I am using vuetify.js navigation-drawer which takes v-model="drawerFlag", where drawerFlag is a computed property. When i tried to use v-model="drawer" i.e. binding to the prop I was getting an error. Then we can close the navigation drawer by clicking an element inside the navigation-drawer. To achieve that, I am calling a method of the component which later on emits an event which is listened by parent component.

Answers:

Answer

I solved it like this:

App.vue

<my-drawer ref="drawer"></my-drawer>
<my-header @toggle-drawer="$refs.drawer.drawer = !$refs.drawer.drawer"></my-header>

MyDrawer.vue

<v-navigation-drawer v-model="drawer">
...
data() {
  drawer: true
}

MyHeader.vue

<v-toolbar-side-icon @click.stop="$emit('toggle-drawer')"></v-toolbar-side-icon>


It seems to me that we need v-model="drawer" on custom drawer component so it can properly work on all screen sizes.

Thus we need to change it's value from parent (or sibling) somehow also, that's why I'm using ref on drawer component. Maybe instead of changing $refs.drawer.drawer data I could call drawers functions instead. I'm not sure what would be better approach. But this was the only simple solution that worked for me on all screen sizes.

So in my case, I'm changing drawer state only from header, but I think you can use this and fit according to your needs.

Answer

I solved the similar problem like below.

  • Using just drawer instead of using drawer and drawerFlag
  • Without having extra button on child component
  • Without emitting event from child to parent
  • Using watch in child component

Home.Vue

<template>
  <div class="full-screen">
    <navigation-bar :drawer="drawer"></navigation-bar>
    <v-btn icon class="mt-3 fixed-position" @click.stop="drawer = !drawer">
      <v-icon>menu</v-icon>
    </v-btn>
  </div>
</template>

<script>
  import NavigationBar from '@/components/NavigationBar';
  export default {
    name: 'home',
    data() {
      return {
        drawer: true
      };
    },
    components: {
      NavigationBar
    }
  }
</script>

NavigationBar.vue

<template>
  <v-navigation-drawer
    temporary
    v-model="drawer"
    light
    overflow
    fixed
  >
    <v-list class="pt-0">
      <template v-for="item in items">
        <v-list-tile :key="item.title" :to="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.title }}</v-list-tile-title>
          </v-list-tile-content>
        </v-list-tile>
        <v-divider></v-divider>
      </template>
    </v-list>
  </v-navigation-drawer>
</template>

<script>
  export default {
    props: ['drawer'],
    data() {
      return {
        items: [
          { title: 'Home', icon: 'home', link: '/home'},
          { title: 'History', icon: 'history', link: '/history' },
          { title: 'Wallet', icon: 'account_balance_wallet', link: '/wallet' },
          { title: 'My Profile', icon: 'person', link: '/profile' },
          { title: 'Settings', icon: 'settings', link: '/settings' },
          { title: 'About', icon: 'error', link: '/about' },
          { title: 'Logout', icon: 'power_settings_new', link: '/logout' },
        ]
      };
    },
    watch: {
       drawer (value) {
          return value;
       }
    }
  }
</script>

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.