How to make vuetify navigation drawer to close group with selected item and open group based on url

I'm trying to make my navigation drawer to act as the following:

  1. When I lick on a v-list-item I want it to close any v-list-group that was opened
  2. When I reload the page I would like to reopen the v-list-group and highlight its v-list-item that is related to the page that the user is. It does work when the user is on the same page as a v-list-item but it doesn't work when the v-list-item is inside my v-list-group.

My navigation drawer:

<v-navigation-drawer v-model="drawer" bottom dense clipped app shapped>
  <v-list nav dense flat v-for="item in navigationDrawerItens" :key="item.title">
    <v-list-item
      v-if="item.items == undefined"
      :to="item.to"
      link
      active-class="primary--text text--primary-4"
      :click="item.click"
    >
      <v-list-item-icon>
        <v-icon>{{item.icon}}</v-icon>
      </v-list-item-icon>
      <v-list-item-title>{{item.title}}</v-list-item-title>
    </v-list-item>
    <v-list-group :prepend-icon="item.icon" no-action v-else>
      <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"
        :to="subItem.to"
        link
        active-class="primary--text text--primary-4"
      >
        <v-list-item-icon>
          <v-icon>{{subItem.icon}}</v-icon>
        </v-list-item-icon>
        <v-list-item-title>{{subItem.title}}</v-list-item-title>
      </v-list-item>
    </v-list-group>
  </v-list>
</v-navigation-drawer>

My navigationDrawerItems:

private navigationDrawerItens = [
{
  title: 'Live',
  icon: 'videocam',
  click: this.getSettingsFromRest(),
  to: '/live'
},
{
  title: 'Settings',
  icon: 'build',
  click: this.getSettingsFromRest(),
  to: '/settings'
},
{
  title: 'About',
  icon: 'info',
  items: [{
    title: 'Contact us',
    to: '/contactus',
    icon: 'contact_support'
  }]
}

]

What I'm trying to say is: If I click on Settings and I was on Contact us, I would like to close the group About. If I was on Contact Us and I refresh the page, I would like to open the group About and Highlight Contact us

Upvotes: 1

Views: 4995

Answers (3)

Benny Ng
Benny Ng

Reputation: 12466

Use group in v-list-group:

Assign a route namespace. Accepts a string or regexp for determining active state

Example

<v-list-group group="products">
  <template v-slot:activator>
    <v-list-item-title>Products</v-list-item-title>
  </template>
  <v-list-item to="/products/123">
    <v-list-item-content> 123 </v-list-item-content>
  </v-list-item>
  <v-list-item to="/products/abc">
    <v-list-item-content> abc </v-list-item-content>
  </v-list-item>
</v-list-group>

Group Products is active when current route matches '/products'.

E.g. http://localhost:3333/products/abc

enter image description here

Upvotes: 1

After chans help I manage to solve it like this:

<v-navigation-drawer v-model="drawer" bottom dense clipped app shapped>
  <v-list nav dense flat v-for="item in navigationDrawerItems" :key="item.title">
    <v-list-item
      v-if="!item.items"
      :to="item.to"
      link
      active-class="primary--text text--primary-4"
      v-on:click="item.click;closeGroupIfActiveAfterClickingOnItem(item)"
    >
      <v-list-item-icon>
        <v-icon>{{item.icon}}</v-icon>
      </v-list-item-icon>
      <v-list-item-title>{{item.title}}</v-list-item-title>
    </v-list-item>
    <v-list-group :prepend-icon="item.icon" no-action v-else v-model="item.active">
      <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"
        :to="subItem.to"
        link
        v-on:click="closeGroupIfActiveAfterClickingOnItem(item)"
        active-class="primary--text text--primary-4"
      >
        <v-list-item-icon>
          <v-icon>{{subItem.icon}}</v-icon>
        </v-list-item-icon>
        <v-list-item-title>{{subItem.title}}</v-list-item-title>
      </v-list-item>
    </v-list-group>
  </v-list>
</v-navigation-drawer>

my code snippet:

private navigationDrawerItems = [
{
  title: 'Live',
  icon: 'videocam',
  click: this.getSettingsFromRest(),
  to: '/live'
},
{
  title: 'Settings',
  icon: 'build',
  click: this.getSettingsFromRest(),
  to: '/settings'
},
{
  title: 'About',
  icon: 'info',
  active: false,
  items: [{
    title: 'Contact us',
    to: '/contactus',
    icon: 'contact_support'
  }]
}]

private closeGroupIfActiveAfterClickingOnItem(pItem: any) {
for (let item of this.navigationDrawerItems) {
  //This is a group
  if (item.items) {
    //Check if group is active AND we are not closing self
    if (item.active && item.title != pItem.title) {
      item.active = false
    }
  } else { continue }
}
}

private checkRoute() {
for (let item of this.navigationDrawerItems) {
  if (item.items) {
    for (let subItem of item.items) {
      if (subItem.to == this.$route.fullPath) {
        item.active = true
        break
      }
    }
  } else { continue }
}
}

Upvotes: 0

chans
chans

Reputation: 5260

Added some additional code for the above mentioned functionalities

Find the working codepen here: https://codepen.io/chansv/pen/QWWpYvK

<div id="app">
  <v-app id="inspire">
    <v-navigation-drawer :clipped="$vuetify.breakpoint.lgAndUp"
      app
    >
      <v-list dense>
        <template v-for="(item, ind) in items">
          <v-list-item
            v-if="!item.children"
            :key="item.text"
            @click="currentSelection = item.text"
            :class="currentSelection == item.text ? 'grey': ''"
          >
            <v-list-item-action>
              <v-icon>{{ item.icon }}</v-icon>
            </v-list-item-action>
            <v-list-item-content>
              <v-list-item-title>
                {{ item.text }}
              </v-list-item-title>
            </v-list-item-content>
          </v-list-item>
          <v-list-group
            v-if="item.children"
            :key="item.text"
            v-model="item.model"
            :prepend-icon="item.model ? item.icon : item['icon-alt']"
            append-icon=""

          >
            <template v-slot:activator>
              <v-list-item>
                <v-list-item-content>
                  <v-list-item-title>
                    {{ item.text }}
                  </v-list-item-title>
                </v-list-item-content>
              </v-list-item>
            </template>
            <v-list-item
              v-for="(child, i) in item.children"
              :key="i"
              @click="item.model = false;currentSelection = child.text"
              :class="currentSelection == child.text ? 'grey': ''"
            >
              <v-list-item-action v-if="child.icon">
                <v-icon>{{ child.icon }}</v-icon>
              </v-list-item-action>
              <v-list-item-content>
                <v-list-item-title>
                  {{ child.text }}
                </v-list-item-title>
              </v-list-item-content>
            </v-list-item>
          </v-list-group>

        </template>
      </v-list>
    </v-navigation-drawer>
    <v-container fluid>
      <v-row justify="center" align="center">
        <v-col cols="12" style="text-align: center;">
          <v-card>
            {{currentSelection}}
          </v-card>
        </v-col>
      <v-row>
    </v-container>
  </v-app>
</div>


new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data: () => ({
    currentSelection: '',
    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: 'More',
        model: false,
        children: [
          { text: 'Import' },
          { text: 'Export' },
          { text: 'Print' },
          { text: 'Undo changes' },
          { text: 'Other contacts' },
        ],
      },
    ],
  }),
  created() {
    this.currentSelection = this.items[0].text;

    // retain the current route from this.$route.name or path and set it to current variable
    var current = ''; // set from this.$route
    // var current = 'Print'; // for testing comment above line and uncomment this line
    if (current) {
      var self = this;
      this.items.forEach((item, i) => {
        if (item.text == current) {
          this.currentSelection = current;
        }
        if (item.children && item.children.length) {
          if (item.children.map(x => x.text).includes(current)) {
            self.items[i].model = true;
            this.currentSelection = current;
          }
        }
      })
    }

  }
})

Upvotes: 3

Related Questions