Skip to content

Commit 08f6407

Browse files
committed
fix: improve leave create/edit view guard (when data is unsaved).
Don't change url, when "Go back" button in browser was clicked many times https://web.tracklify.com/project/2b7ZVgE5/AdminForth/1285/pPkdEPJQ/strange-behaviour-of-on-before
1 parent 4cacd7d commit 08f6407

File tree

3 files changed

+62
-25
lines changed

3 files changed

+62
-25
lines changed

adminforth/spa/src/utils/utils.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ import router from "../router";
55
import { useCoreStore } from '../stores/core';
66
import { useUserStore } from '../stores/user';
77
import { Dropdown } from 'flowbite';
8-
import adminforth from '../adminforth';
8+
import adminforth, { useAdminforth } from '../adminforth';
99
import sanitizeHtml from 'sanitize-html'
1010
import debounce from 'debounce';
1111
import type { AdminForthResourceColumnInputCommon, Predicate } from '@/types/Common';
1212
import { i18nInstance } from '../i18n'
1313
import { useI18n } from 'vue-i18n';
14-
14+
import { onBeforeRouteLeave } from 'vue-router';
1515

1616

1717

@@ -616,4 +616,55 @@ export function getTimeAgoString(date: Date): string {
616616
const days = Math.floor(diffInSeconds / 86400);
617617
return `${days} ${days === 1 ? 'day' : 'days'} ago`;
618618
}
619+
}
620+
621+
export class leaveGuardActiveClass {
622+
private active = false;
623+
624+
isActive() {
625+
return this.active;
626+
}
627+
628+
setActive(value: boolean) {
629+
this.active = value;
630+
}
631+
}
632+
633+
export async function onBeforeRouteLeaveCreateEditViewGuard(initialValues: any, record: any, checkIfWeCanLeavePage: () => boolean, leaveGuardActive: leaveGuardActiveClass): Promise<boolean> {
634+
635+
const { confirm } = useAdminforth();
636+
const { t } = useI18n();
637+
638+
onBeforeRouteLeave(async (to, from) => {
639+
640+
if (leaveGuardActive.isActive()) {
641+
return false;
642+
}
643+
644+
if (checkIfWeCanLeavePage()) {
645+
return true;
646+
}
647+
648+
leaveGuardActive.setActive(true);
649+
650+
try {
651+
const { changedFields } = compareOldAndNewRecord(
652+
initialValues.value,
653+
record.value
654+
);
655+
656+
const messageHtml =
657+
generateMessageHtmlForRecordChange(changedFields, t);
658+
659+
const answer = await confirm({
660+
messageHtml,
661+
yes: t('Yes'),
662+
no: t('No'),
663+
});
664+
665+
return answer;
666+
} finally {
667+
leaveGuardActive.setActive(false);
668+
}
669+
});
619670
}

adminforth/spa/src/views/CreateView.vue

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
7979
import ResourceForm from '@/components/ResourceForm.vue';
8080
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
8181
import { useCoreStore } from '@/stores/core';
82-
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown, checkShowIf, compareOldAndNewRecord, generateMessageHtmlForRecordChange } from '@/utils';
82+
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown, checkShowIf, compareOldAndNewRecord, onBeforeRouteLeaveCreateEditViewGuard, leaveGuardActiveClass, onBeforeRouteLeaveCreateEditView } from '@/utils';
8383
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
8484
import { onMounted, onBeforeMount, onBeforeUnmount, ref, watch, nextTick } from 'vue';
8585
import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router';
@@ -137,17 +137,12 @@ onBeforeUnmount(() => {
137137
window.removeEventListener('beforeunload', onBeforeUnload);
138138
});
139139
140-
onBeforeRouteLeave(async (to, from, next) => {
141-
if (!checkIfWeCanLeavePage()) {
142-
const { changedFields } = compareOldAndNewRecord(initialValues.value, record.value);
143-
144-
const messageHtml = generateMessageHtmlForRecordChange(changedFields, t);
145140
146-
const answer = await confirm({ messageHtml: messageHtml, yes: t('Yes'), no: t('No') });
147-
if (!answer) return next(false);
148-
}
149-
next();
150-
});
141+
const leaveGuardActive = new leaveGuardActiveClass();
142+
143+
144+
onBeforeRouteLeaveCreateEditViewGuard(initialValues, record, checkIfWeCanLeavePage, leaveGuardActive);
145+
151146
152147
onBeforeMount(() => {
153148
clearSaveInterceptors(route.params.resourceId as string);

adminforth/spa/src/views/EditView.vue

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
7474
import ResourceForm from '@/components/ResourceForm.vue';
7575
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
7676
import { useCoreStore } from '@/stores/core';
77-
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown, compareOldAndNewRecord, generateMessageHtmlForRecordChange } from '@/utils';
77+
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown, compareOldAndNewRecord, generateMessageHtmlForRecordChange, leaveGuardActiveClass, onBeforeRouteLeaveCreateEditViewGuard } from '@/utils';
7878
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
7979
import { computed, onMounted, onBeforeMount, ref, type Ref, nextTick, watch, onBeforeUnmount } from 'vue';
8080
import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router';
@@ -122,17 +122,8 @@ onBeforeUnmount(() => {
122122
window.removeEventListener('beforeunload', onBeforeUnload);
123123
});
124124
125-
onBeforeRouteLeave(async (to, from, next) => {
126-
if (!checkIfWeCanLeavePage()) {
127-
const { changedFields } = compareOldAndNewRecord(initialRecord.value, record.value);
128-
129-
const messageHtml = generateMessageHtmlForRecordChange(changedFields, t);
130-
131-
const answer = await confirm({ messageHtml: messageHtml, yes: t('Yes'), no: t('No') });
132-
if (!answer) return next(false);
133-
}
134-
next();
135-
});
125+
const leaveGuardActive = new leaveGuardActiveClass();
126+
onBeforeRouteLeaveCreateEditViewGuard(initialRecord, record, checkIfWeCanLeavePage, leaveGuardActive);
136127
137128
const resourceFormRef = ref<InstanceType<typeof ResourceForm> | null>(null);
138129

0 commit comments

Comments
 (0)