admin管理员组文章数量:1415484
I have a VueJS/Vuetify application that has tab bars using v-tabs
/v-tab
ponents to navigate between pages. I have implemented code using the click
event in the v-tab
element that checks to make sure there is no unsaved content when the user clicks on another tab, and if there is, displays a modal using v-dialog
to alert the user. If the user chooses to continue, it continues on to the desired tab/ponent. However, if the user selects Cancel
in the modal, the page is left where it was.
Here is the tabs ponent:
<template>
<div>
<!-- Tabs -->
<v-tabs
color="secondary"
:value="currentTab"
>
<v-tab
v-for="(tab, i) in userTabs"
:key="i"
:href="tab.href"
@click="tabClick(tabponent, tab.link)"
:disabled="isDisabled(tab)"
>
{{ tab.title }}
</v-tab>
</v-tabs>
<BaseConfirmModal
:value="showUnsaved"
:title="unsavedContentTitle"
:text="unsavedContentText"
declineText="Cancel"
@clicked="unsavedModalConfirm"
/>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
import baseTabMixin from '@/ponents/mixins/workspace/baseTabMixin';
export default {
name: 'UserTabs',
data: () => ({
userTabs: [
{
title: 'General Info',
href: '#tab-general',
link: 'tab-general',
ponent: 'UserEdit',
},
{
title: 'Enrollments',
href: '#tab-enrollments',
link: 'tab-enrollments',
ponent: 'UserEnrollmentEdit',
},
{
title: 'Alerts',
href: '#tab-alerts',
link: 'tab-alerts',
ponent: 'UserAlertEdit',
},
{
title: 'Devices',
href: '#tab-devices',
link: 'tab-devices',
ponent: 'UserDeviceEdit',
},
],
}),
puted: {
...mapGetters('app', ['getStickyTenant', 'roleAtLeastTa', 'getUnsaved']),
...mapGetters('users', ['getCurrent']),
...mapGetters('tabs', {
currentTab: 'getSelected',
}),
},
methods: {
...mapActions('tabs', {
setCurrentTab: 'setSelected',
}),
isDisabled(item) {
if (itemponent === 'UserEdit') {
return false;
}
if (itemponent === 'UserDeviceEdit' && !this.roleAtLeastTa) {
return true;
}
return !this.getCurrent.userId;
},
},
mixins: [baseTabMixin],
};
</script>
and the referenced baseTabMixin
:
import { mapGetters, mapActions } from 'vuex';
const baseTabMixin = {
data: () => ({
showUnsaved: false,
unsavedContentTitle: 'Unsaved Changes',
unsavedContentText: 'You have made changes to this page that are not saved. Do you wish to continue?',
destTabComponent: '',
destTabLink: '',
}),
ponents: {
BaseConfirmModal: () => import('@/ponents/base/BaseConfirmModal'),
},
puted: {
...mapGetters('app', ['getUnsaved']),
},
methods: {
...mapActions('app', ['setUnsaved']),
tabClick(ponent, tab) {
// Check to see if getUnsaved === true; if it is,
// set variable to display warning modal.
if (this.getUnsaved) {
this.showUnsaved = true;
} else {
// There is no unsaved content, so continue to the desired tab.
this.destTabComponent = ponent;
this.destTabLink = tab;
this.setCurrentTab(tab);
this.$router.push({ name: ponent });
}
},
unsavedModalConfirm(confirm) {
if (confirm) {
this.setCurrentTab(this.destTabLink);
this.$router.push({ name: this.destTabComponent });
}
this.showUnsaved = false;
},
},
};
export default baseTabMixin;
The problem has to do with the tab item highlighting. When the new tab is clicked, the slider moves to the new tab and the new tab title is bolded before the click event (tabClick()
in this case) is called. When I select Cancel
in my modal, it leaves the page where it is (as expected), but the clicked tab is still highlighted, both with the slider underneath and the bolder text. Since that all happens before my click handler is even called, is there way to either a) stop the highlighting from happening before the click event is called, or b) reverse the highlighting back to the current tab?
I have a VueJS/Vuetify application that has tab bars using v-tabs
/v-tab
ponents to navigate between pages. I have implemented code using the click
event in the v-tab
element that checks to make sure there is no unsaved content when the user clicks on another tab, and if there is, displays a modal using v-dialog
to alert the user. If the user chooses to continue, it continues on to the desired tab/ponent. However, if the user selects Cancel
in the modal, the page is left where it was.
Here is the tabs ponent:
<template>
<div>
<!-- Tabs -->
<v-tabs
color="secondary"
:value="currentTab"
>
<v-tab
v-for="(tab, i) in userTabs"
:key="i"
:href="tab.href"
@click="tabClick(tab.ponent, tab.link)"
:disabled="isDisabled(tab)"
>
{{ tab.title }}
</v-tab>
</v-tabs>
<BaseConfirmModal
:value="showUnsaved"
:title="unsavedContentTitle"
:text="unsavedContentText"
declineText="Cancel"
@clicked="unsavedModalConfirm"
/>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
import baseTabMixin from '@/ponents/mixins/workspace/baseTabMixin';
export default {
name: 'UserTabs',
data: () => ({
userTabs: [
{
title: 'General Info',
href: '#tab-general',
link: 'tab-general',
ponent: 'UserEdit',
},
{
title: 'Enrollments',
href: '#tab-enrollments',
link: 'tab-enrollments',
ponent: 'UserEnrollmentEdit',
},
{
title: 'Alerts',
href: '#tab-alerts',
link: 'tab-alerts',
ponent: 'UserAlertEdit',
},
{
title: 'Devices',
href: '#tab-devices',
link: 'tab-devices',
ponent: 'UserDeviceEdit',
},
],
}),
puted: {
...mapGetters('app', ['getStickyTenant', 'roleAtLeastTa', 'getUnsaved']),
...mapGetters('users', ['getCurrent']),
...mapGetters('tabs', {
currentTab: 'getSelected',
}),
},
methods: {
...mapActions('tabs', {
setCurrentTab: 'setSelected',
}),
isDisabled(item) {
if (item.ponent === 'UserEdit') {
return false;
}
if (item.ponent === 'UserDeviceEdit' && !this.roleAtLeastTa) {
return true;
}
return !this.getCurrent.userId;
},
},
mixins: [baseTabMixin],
};
</script>
and the referenced baseTabMixin
:
import { mapGetters, mapActions } from 'vuex';
const baseTabMixin = {
data: () => ({
showUnsaved: false,
unsavedContentTitle: 'Unsaved Changes',
unsavedContentText: 'You have made changes to this page that are not saved. Do you wish to continue?',
destTabComponent: '',
destTabLink: '',
}),
ponents: {
BaseConfirmModal: () => import('@/ponents/base/BaseConfirmModal'),
},
puted: {
...mapGetters('app', ['getUnsaved']),
},
methods: {
...mapActions('app', ['setUnsaved']),
tabClick(ponent, tab) {
// Check to see if getUnsaved === true; if it is,
// set variable to display warning modal.
if (this.getUnsaved) {
this.showUnsaved = true;
} else {
// There is no unsaved content, so continue to the desired tab.
this.destTabComponent = ponent;
this.destTabLink = tab;
this.setCurrentTab(tab);
this.$router.push({ name: ponent });
}
},
unsavedModalConfirm(confirm) {
if (confirm) {
this.setCurrentTab(this.destTabLink);
this.$router.push({ name: this.destTabComponent });
}
this.showUnsaved = false;
},
},
};
export default baseTabMixin;
The problem has to do with the tab item highlighting. When the new tab is clicked, the slider moves to the new tab and the new tab title is bolded before the click event (tabClick()
in this case) is called. When I select Cancel
in my modal, it leaves the page where it is (as expected), but the clicked tab is still highlighted, both with the slider underneath and the bolder text. Since that all happens before my click handler is even called, is there way to either a) stop the highlighting from happening before the click event is called, or b) reverse the highlighting back to the current tab?
- have you tried @click.stop="tabCli... on your tab ponent? – whmkr Commented Dec 4, 2019 at 1:30
-
1
@wheelmaker Yes, I tried both
@click.stop
and@click.prevent
, and neither made a difference. – wonder95 Commented Dec 4, 2019 at 17:28
2 Answers
Reset to default 4Link to working pen.
Note the key parts of the code :-
<v-tabs :value="currentTab" @change="onTabChange">
<v-tab v-for="(tab, i) in tabs" :key="i">{{tab.title}}</v-tab>
</v-tabs>
async onTabChange(clickedTab)
{
this.currentTab = clickedTab;
await this.$nextTick();
if (!this.allowTabChange) this.currentTab = this.previousTab;
else this.previousTab = this.currentTab;
}
v-tabs
ponent uses an internal model to maintain it's state i.e. which tab is currently active. Using the :value
attribute, we can set an initial active tab. When the user clicks on a different tab, the internal model is updated leading to the clicked tab getting highlighted. v-tabs
also emits a change event to notify the parent ponent of this change. We need to listen for this event and manage the :value
variable by ourselves to maintain state in the parent. Since v-model
is an abstraction over :value
and @change
, we could use that as well.
To listen for this change event, add @change
to v-tabs
. You can shift the entire logic that you have at @click
to @change
resulting in a cleaner code.
To prevent selection of the next tab, we need to use some tricky code since the internal model of v-tabs
is not accessible to us. We need to allow currentTab
to change temporarily to the clicked tab and then we reset it to previousTab
in $nextTick
i.e. after the current batch of updates has been picked up by Vue. The highlight beneath the tabs itself occurs after $nextTick
so it will not change since we have reset the :value
to previousTab
by then.
If you are ok with letting the highlight temporarily shift to the new tab, then all you need to do is update currentTab
to the index of next tab and the highlight will shift over as normal. Then depending on the decision made by user, you can set currentTab
to previousTab
to revert the change or you can update previousTab
with the new value of currentTab
i.e. the next tab. In this case the $nextTick
hack is unnecessary.
Thanks to the answer by @ParaBolt and his CodePen, I got this working. My answer differed slightly from his because my currentTab
is stored in state, not the ponent, and I also used a filter
method to get the ponent
value, but the structure is the same.
Here are the important parts of the mixin:
data: () => ({
..
previousTab: '',
}),
methods: {
...mapActions('app', ['setUnsaved']),
async tabChange(newTab) {
this.setCurrentTab(newTab);
// We need to get the corresponding ponent value from the tabs array.
const newComponent = this.tabList.filter(tab => tab.link === newTab)[0].ponent;
// Stash these in case we need then in unsavedModalConfirm()
this.destTabComponent = newComponent;
this.destTabLink = newTab;
// Check to see if getUnsaved === true; if it is,
// set variable to display warning modal.
await this.$nextTick();
if (this.getUnsaved) {
this.setCurrentTab(this.previousTab);
this.showUnsaved = true;
} else {
// There is no unsaved content, so continue to the desired tab.
this.setCurrentTab(newTab);
this.$router.push({ name: newComponent });
}
},
unsavedModalConfirm(confirm) {
if (confirm) {
// User selected Continue from modal.
this.setCurrentTab(this.destTabLink);
this.$router.push({ name: this.destTabComponent });
this.setUnsaved(false);
} else {
// User selected Cancel
this.setCurrentTab(this.previousTab);
}
this.showUnsaved = false;
},
},
mounted() {
this.previousTab = this.currentTab;
},
Whenever I cancel from the modal, it just stays where it is, and if I choose to continue, it goes on to the desired tab.
本文标签: javascriptStop or reverse highlighting of clicked Vuetify vtab itemStack Overflow
版权声明:本文标题:javascript - Stop or reverse highlighting of clicked Vuetify v-tab item - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745150215a2644885.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论