Guide to implementing Vuetify tabs alongside Vue Router

I have a scenario with two Vuetify tabs in this jsfiddle, but the documentation lacks examples of using vue-router with them.

After stumbling upon a helpful Medium.com post about integrating Vuetify with vue-router, I came across the following code:

<div id="app">
  <v-tabs grow light>
    <v-tabs-bar>
      <v-tabs-item href="/" router>
        <v-icon>motorcycle</v-icon>
      </v-tabs-item>
      <v-tabs-item href="/dog" router>
        <v-icon>pets</v-icon>
      </v-tabs-item>
    </v-tabs-bar>
  </v-tabs>
      
  <router-view />
</div>

Unfortunately, this code is no longer valid as the Vuetify 1.0.13 Tabs documentation doesn't mention a router prop in their API, rendering the example from the post useless.

Further searching led me to a helpful StackOverflow answer with this code snippet:

<v-tabs-item :to="{path:'/path/to/somewhere'}">

However, using the to prop proved ineffective, and it isn't even listed in the Vuetify API. Interestingly, the v-button Vuetify component does have a to prop that works with vue-router, raising the question why a similar support is not provided for Vuetify tabs.

Upon delving into the ancient Vuetify 0.17 documentation, I found a mention of the to prop for v-tabs-item, suggesting that support for it may have been removed in version 1.0.13.

So, the big question remains: How can one effectively utilize vue-router with Vuetify tabs?

Answer №1

Newsflash

Unbelievable! I reached out to the Vuetify community asking for more documentation on their API, and they actually just updated the to prop along with other vue-router props in the Vuetify tabs documentation. What an amazing community they have there!

Initial Response

The amazing individuals in the Vuetify community Discord were able to lend a helping hand. My revised jsfiddle now showcases the functioning code.

Basically, v-tab acts as a container for router-link, likely utilizing slots to pass props to router-link, so adding the to prop on v-tab works seamlessly.

Below is an example snippet of the functional code:

html

<v-app dark>
  <v-tabs fixed-tabs>
    <v-tab to="/foo">Foo</v-tab>
    <v-tab to="/bar">Bar</v-tab>
  </v-tabs>
  <router-view></router-view>
</v-app>

js

const Foo = {
  template: "<div>Foo component!</div>",
};

const Bar = {
  template: "<div>Bar component!</div>",
};

const routes = [
  { path: "/foo", component: Foo },
  { path: "/bar", component: Bar },
];

const router = new VueRouter({ routes });

new Vue({
  el: "#app",
  router,
});

Outcome

https://i.sstatic.net/TiiVS.png https://i.sstatic.net/HqxGA.png

Answer №2

The code snippet:

<div>
    <v-tabs
      class="tabs"
      centered
      grow
      height="60px"
      v-model="activeTab"
    >
      <v-tab v-for="tab in tabs" :key="tab.id" :to="tab.route" exact>
        {{ tab.name }}
      </v-tab>
    </v-tabs>
    <router-view></router-view>
</div>

And the JavaScript section:

  data() {
    return {
      activeTab: `/user/${this.id}`,
      tabs: [
        { id: 1, name: "Task", route: `/user/${this.id}` },
        { id: 2, name: "Project", route: `/user/${this.id}/project` }
      ]
    };
  }

Routing information:

{
  path: "/user/:id",
  component: User1,
  props: true,
  children: [
    {
      path: "", //default selected tab
      component: TaskTab
    },
    {
      path: "project",
      component: ProjectTab
    }
  ]
}

View codesandbox example

Answer №3

Adding some tips on animations and explaining the difference between v-tab-items and v-tab-item.

If you've noticed, the following markup will prevent the v-tabs tab switch animation from working:

<v-tabs v-model="activeTab">
  <v-tab key="tab-1" to="/tab-url-1">tab 1</v-tab>
  <v-tab key="tab-2" to="/tab-url-2">tab 2</v-tab>
</v-tabs>
<router-view />

If you want the tab switch animation to work, here's one way to achieve it.

<v-tabs v-model="activeTab">
  <v-tab key="tab-1" to="/tab-url-1" ripple>
    tab 1
  </v-tab>
  <v-tab key="tab-2" to="/tab-url-2" ripple>
    tab 2
  </v-tab>

  <v-tab-item id="/tab-url-1">
    <router-view v-if="activeTab === '/tab-url-1'" />
  </v-tab-item>
  <v-tab-item id="/tab-url-2">
    <router-view v-if="activeTab === '/tab-url-2'" />
  </v-tab-item>
</v-tabs>

You can also use a v-for loop on your v-tab and v-tab-item tags, as long as the value of to is found among the id attribute of your v-tab-items.

If you need to separate your tab contents from the tab buttons, you can use v-tab-items. Place the v-tab-items in a v-tab-items tag outside the v-tabs component, ensuring you give it a v-model="activeTab" attribute.

Answer №4

Integrating Animations and Swipe Functionality

Retain your current query parameters for enhanced usability, especially if your URL includes a :locale parameter:

Template

<v-tabs v-model="activeTab">
   <v-tab v-for="tab in tabs" :key="tab.id" :to="tab.to">
    {{ tab.name }}
  </v-tab>
</v-tabs>

<v-tabs-items v-model="activeTab" @change="updateRouter($event)">
  <v-tab-item v-for="tab in tabs" :key="tab.id" :value="tab.to">
    <router-view />          
  </v-tab-item>
</v-tabs-items>

Script

export default {
  data: () => ({
    activeTab: '',
  }),
  computed: {
    tabs() {
      return [
        { id: 1, name: 'Tab one', to: this.getTabPath('routeName1') },
        { id: 2, name: 'Tab two', to: this.getTabPath('routeName2') },
        { id: 3, name: 'Tab three', to: this.getTabPath('routeName3') },
        { id: 4, name: 'Tab four', to: this.getTabPath('routeName4') },
      ]
    },
  },
  methods: {
    getTabPath(name) {
      // Retrieve the path while maintaining parameters. Useful when handling a :locale in the URL:
      return this.$router.resolve({ name, params: this.$route.params }).href
    },
    updateRouter(path) {
      // Executed when swiping occurs.
      this.$router.push(path)
    },
  },
}

Utilizing $router.resolve to acquire the path from a route object, detailed explanation provided here.

Answer №5

Both @roli-roli and @antoni have provided accurate answers, but they are missing a crucial detail. While their methods are almost equivalent, there is an issue with mobile views; specifically, tabs become swipeable in these conditions. However, swiping does not update the route as expected, causing a transition from Tab A with component A to Tab B with component B to result in an animation and a change in the activeTab, but not an update in the router.

TL;DR Swiping the v-tab-item does not update the router, resulting in the same component being displayed under each tab.

Possible solutions include disabling the swipeability of v-tab-item or listening for the change event of the tab component to update the router accordingly. The latter solution is recommended (as swiping can be useful in certain scenarios) , but it may cause the router to update twice when clicking on the tab's label.

Here's a functional example based on @roli-roli's answer

Template

<template>
    <v-tabs v-model="activeTab">
        <v-tab v-for="tab in tabs" :key="tab.id" :to="tab.route">{{ tab.name }}</v-tab>

        <v-tabs-items v-model="activeTab" @change="updateRouter($event)">
            <v-tab-item v-for="tab in tabs" :key="tab.id" :to="tab.route">
                <router-view />          
            </v-tab-item>
        </v-tabs-items>
    </v-tabs>
</template>

Script

export default {
    data: () => ({
        activeTab: '',
        tabs: [
            {id: '1', name: 'Tab A', route: 'component-a'},
            {id: '2', name: 'Tab B', route: 'component-b'}
        ]
    }),
    methods: {
        updateRouter(val){
            this.$router.push(val)
        }
    }
}

Router

Configure as per previous responses.

Answer №6

// Hyperlinks within a component
 <v-btn @click="goToUrl({name: 'RouteName1'})">
    .....
<v-list-item-title 
    @click="goToUrl({name: 'RouteName2'})"
>
    Some tab link text
 </v-list-item-title>
    ....


// Tab menu and its content in another component
<v-tabs>
    <v-tab key="key_name1" :to="{ name: 'RouteName1'}">Some link 1</v-tab>
    <v-tab key="key_name2" :to="{ name: 'RouteName2'}">Some link 2</v-tab>

<v-tab-item key="key_name1" value="/url/for/route1">
    ....
<v-tab-item key="key_name2" value="/url/for/route2">
    ....

Answer №7

Here's a solution to prevent double mounting of a new component when switching tabs. For more details, check out the question and answer here.

In short, using a v-for within the div can cause the component to be created, destroyed, and created again with each tab switch. This can lead to unnecessary calls to the backend. By wrapping the div with a unique key, you can avoid this issue.

I delve deeper into the reasons behind this behavior in the answer.

Answer №8

This solution has been effective for me

  <v-tabs fixed-tabs>
          <v-tab>Categories</v-tab>
          <v-tab>Products</v-tab>
          <v-tab-item>Tab 1 content
            <categories-data/>
          </v-tab-item>
          <v-tab-item>Tab 2 content
            <products-data/>
          </v-tab-item>
    </v-tabs>
   <script>
        import CategoriesData from './Inventory/Categories';
        import ProductsData from './Inventory/Products';
        export default {
           components: {
            CategoriesData,
            ProductsData
          },
        }
   </script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The v-for directive is causing issues with loading the array

Currently, I am attempting to iterate through a group of users sourced from the jsonplaceholder API. Below is the code for my Vue component: new Vue({ el: "#vue-app", data: { name: "dani", thumbnail: '', users: [] }, beforeM ...

What is the best way to verify the font-family using JavaScript?

To verify if the user has installed the font family, we can run a JavaScript function with AngularJS. I am using a Typekit font and have only loaded this service for users who do not have this specific font. ...

Creating a buffered transformation stream in practice

In my current project, I am exploring the use of the latest Node.js streams API to create a stream that buffers a specific amount of data. This buffer should be automatically flushed when the stream is piped to another stream or when it emits `readable` ev ...

What causes req.body to display as an empty object in the console during a POST request?

Having some trouble with my code. The middleware is supposed to return parsed data in the console as an object accessed by req.body, but it keeps returning an empty object for some reason. Check out Code and Console Snapshot 1 View Code and Console Snapsh ...

The technique of binding methods in React

When working with React.js, it's recommended to define your method binding in the constructor for better performance. Here's an example: constructor(props){ this.someFunction = this.someFunction.bind(this); } This approach is more efficient t ...

encountering an issue while working with Selenium on Google Colab

I'm attempting to perform web scraping with selenium in Google Colab and running into some errors https://i.sstatic.net/egDf7.png The webpage is prompting me to enable JavaScript and disable any ad blockers. I tried enabling JavaScript by adding the ...

Tips for successfully installing a package through npm

Looking to set up nest.js Using the guide provided below. https://www.npmjs.com/package/@nestjs/cli Attempted the following command $ npm install -g @nestjs/cli Encountered this error message. bash: /usr/local/bin/npm: No such file or directory Any ...

notification that appears when checkbox is ticked

If a certain delivery option is selected during the checkout process on our website, we would like to trigger a popup box displaying a custom message. Here is the specific checkbox for that delivery option: <input id="delivery_option_22_6" class="deliv ...

Validating URL patterns in JavaScript using Ajax

Below are the ajax urls displayed in a specific format: http://example.com/v1/components/compId http://example.com/v1/machine/machineId http://example.com/v1/graph/startTime=value?endtime=value http://example.com/v1/graph/startDate=value?enddate=value? ...

Managing and retrieving data in bulk from an indexed database as JSON format

I am currently developing an application that utilizes IndexexDB for local data storage. I am looking for a way to extract and insert large amounts of data from an indexed DB in JSON format. Below is the code snippet illustrating what I have accomplished s ...

The Javascript and CSS code for a button fails to trigger the animation function after the initial click

Is it possible to trigger the animation function multiple times after the initial click without making changes to the HTML code? The ID and class in the code must remain as they are. I attempted to use the display property set to none, but it did not yie ...

What steps do I need to take to integrate my RASA assistant into my personal website?

Deploying my rasa chatbot on my live website is my next step. While Rasa worked smoothly on my localhost server, as a newcomer to web development, I found the official guide provided by RASA in the link below a bit challenging to comprehend: The RASA guid ...

The useEffect() method used without any cleanup function

It is mentioned that, "Every time our component renders, the effect is triggered, resulting in another event listener being added. With repeated clicks and re-renders, numerous event listeners are attached to the DOM! It is crucial to clean up after oursel ...

Utilize Lodash to dynamically filter data based on nested filter conditions

To enhance my product data filtering capabilities, I aim to implement multiple dynamic filter conditions. The initial format of my raw data is structured as follows: [{ ..., "_source": { ..., "categories": [ "home", "office" ], ..., ...

fetching numerous JSON documents using jquery

I am struggling to retrieve data from multiple JSON files and display it in a table. Despite being successful in appending data from one JSON file, I encountered issues when trying to pull data from multiple files. Here is my code: var uri = 'sharepo ...

Rejuvenating your HTML content with AJAX over time

My HTML page contains links to charts that refresh every time the page is reloaded. A friend mentioned that AJAX can automatically refresh the chart at specified intervals without reloading the entire HTML page. I would appreciate any help with the HTML ...

Updating $scope in AngularJS works in JavaScript, but the changes are not reflected in the HTML

After making a request to the server and receiving a correct response, I have an array of objects called $scope.photos. However, when attempting to add a new photo by sending a request and then trying two different methods to update the $scope in the HTML, ...

Problematic situation when using ng-repeat with dynamic ng-include and template variables scope conflict

When utilizing ng-repeat with dynamic ng-include that includes variables, the variables are not being properly recognized. Take a look at this code snippet. Here is the main HTML code: <table style> <tr> <th>Student</th> ...

What are the steps to integrate and utilize DefinePlugin within your webpack configuration?

Hello, I'm currently trying to implement the DefinePlugin in order to update the version number and ensure that my JavaScript refreshes after a new build is released. Unfortunately, I am encountering issues with getting DefinePlugin to work properly. ...

Guide to establishing a primary filter for my Json information with Angular.js?

After spending multiple days searching and reading, I am struggling to set an initial value for the data from a Rails JSON file in my application. The app focuses on incident tickets, and although I am able to retrieve all entries from the database using d ...