skip to Main Content

I am using PrimeVue V3 TabView with composition API.

Inside each tab I have a form and I want to trigger form validation before switching tabs. So I need to interrupt the tab change, do something (form validation), and then conditionally proceed to the clicked tab.

The TabView docs show 3 events: update:activeIndex, tab-change, and tab-click. But as far as I can tell, all of those are triggered after changing tabs, not before.

Any idea how to go this working?

I found a similar question and answer based on the old Options API but I cannot get it working for Vue 3 Composition API.

Here is a live reproduction on stackblitz.

<template>
  <TabView
    v-model:activeIndex="activeIndex"
    @tab-change="handleTabChange"
    @tab-click="handleTabClick"
  >
    <TabPanel header="Opening Hours"> Opening hours form goes here </TabPanel>
    <TabPanel header="Contacts"> Contacts form goes here </TabPanel>
  </TabView>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import type { TabViewChangeEvent, TabViewClickEvent } from 'primevue/tabview';

const activeIndex = ref();

const handleTabClick = async (event: TabViewClickEvent) => {
  console.log('handleTabClick', event);
  const nextTabIndex = event.index;
  const isValid = false; // Simulated form validation
  if (isValid) {
    // Form is valid, continue with the tab change
    console.log('form valid');
    activeIndex.value = nextTabIndex;
  } else {
    // Form is invalid, do nothing (don't change tabs)
    console.log('form invalid');
  }
};

const handleTabChange = async (event: TabViewChangeEvent) => {
  console.log('handleTabChange', event);
  const nextTabIndex = event.index;
  const isValid = false; // Simulated form validation
  if (isValid) {
    // Form is valid, continue with the tab change
    console.log('form valid');
    activeIndex.value = nextTabIndex;
  } else {
    // Form is invalid, do nothing (don't change tabs)
    console.log('form invalid');
  }
};
</script>

2

Answers


  1. Chosen as BEST ANSWER

    This version does not require changing of the inner onTabClick method.

    See live reproduction here.

    <template>
      <TabView v-model:activeIndex="navigateToIndex">
        <TabPanel header="Opening Hours">
          Do some validation on this tab before navigating away
        </TabPanel>
        <TabPanel header="Contacts"> I am another tab </TabPanel>
      </TabView>
    </template>
    
    <script setup lang="ts">
    import { ref, watch } from 'vue';
    
    // This tracks the tab index of where we want to navigate
    const navigateToIndex = ref(0);
    
    // This tracks the current tab index
    const currentTabIndex = ref(navigateToIndex.value);
    
    // Whenever we click a tab navigateToIndex will update
    watch(
      navigateToIndex,
      (newIndex) => {
        // If the new index is the same as the current index, do nothing
        if (newIndex === currentTabIndex.value) return;
    
        // If we are on the first tab
        if (currentTabIndex.value === 0) {
          // Simulated form validation
          // Switch between true and false for testing different scenarios
          const isValid = false;
    
          // Update the indices based on the form validation
          currentTabIndex.value = isValid ? newIndex : 0;
          // Trigger the navigation to the index (either the newIndex or stay on index 0)
          navigateToIndex.value = currentTabIndex.value;
        } else {
          // Update the current index for all other cases
          currentTabIndex.value = newIndex;
        }
      },
      {
        flush: 'post',
      }
    );
    
    // NOTE: Notice the `flush: 'post'` option in the watcher, without it the change in the `activeIndex` back to the original tab (if the form was invalid) wasn't visually taking effect, but the ref was indeed changing. This part was not needed in my codebase, only here on stackblitz.
    </script>
    

  2. I just made the old Options API answer workable for you

    enter image description here

    Here is the code

    <template>
      <TabView
        v-model:activeIndex="activeIndex"
        @tab-change="handleTabChange"
        @tab-click="handleTabClick"
      >
        <TabPanel header="Opening Hours"> Opening hours form goes here </TabPanel>
        <TabPanel header="Contacts"> Contacts form goes here </TabPanel>
      </TabView>
    </template>
    
    <script setup lang="ts">
    import { ref } from 'vue';
    import TabView from 'primevue/tabview';
    import TabPanel from 'primevue/tabpanel';
    import type { TabViewChangeEvent, TabViewClickEvent } from 'primevue/tabview';
    
    // Override the default onTabClick method of TabView
    // This should ideally be in a global setup file, not in a component
    TabView.methods.onTabClick = function (event, tab, i) {
      this.$emit('tab-click', {
        originalEvent: event,
        tabInfo: tab,
        index: i,
      });
    };
    
    // Ref to keep track of the active tab index
    const activeIndex = ref();
    
    // Handler for tab click events
    const handleTabClick = async (event: TabViewClickEvent) => {
      console.log('handleTabClick', event);
      const nextTabIndex = event.index;
    
      // Simulated form validation
      // In a real scenario, this would be replaced with actual form validation logic
      const isValid = false; 
    
      if (isValid) {
        // If the form is valid, allow the tab change
        console.log('form valid');
        activeIndex.value = nextTabIndex;
      } else {
        // If the form is invalid, prevent the tab change
        console.log('form invalid');
        // No action needed here as we're not changing activeIndex
      }
    };
    
    // Handler for tab change events
    const handleTabChange = async (event: TabViewChangeEvent) => {
      console.log('handleTabChange', event);
      
      // Prevent the default tab change behavior
      event.originalEvent.preventDefault();
    
      const nextTabIndex = event.index;
    
      // Simulated form validation
      // In a real scenario, this would be replaced with actual form validation logic
      const isValid = false; 
    
      if (isValid) {
        // If the form is valid, allow the tab change
        console.log('form valid');
        activeIndex.value = nextTabIndex;
      } else {
        // If the form is invalid, prevent the tab change
        console.log('form invalid');
        // No action needed here as we've already prevented the default behavior
      }
    };
    </script>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search