Environment
There’s a legacy nuxt
(v2.14.6
) project with vue-route
(v3.4.3
), module nuxt-i18n
(v6.15.1
), and vuetify
(v2.7.2
). Indeed… Some history here!
A page change there is being done via push:
<!-- File: ./pages/user/_slug/index.vue -->
<script lang="ts">
import mixins from 'vue-typed-mixins';
import SomeMixin from '~/mixins/SomeMixin';
export default mixins(SomeMixin).extend({
name: 'UserPage',
// ...
methods: {
async submit(): Promise<void> {
const response = await this.$api(/* ... */) // ...
// ...
this.$router.push(
this.localePath({
name: 'user-slug-management',
params: {
slug: String(this.user.slug ?? this.user.id)
}
})
);
}
// ...
}
// ...
});
</script>
Another page has the path name in head
:
<!-- File: ./pages/user/_slug/management.vue -->
<template>
<div>
<SomeComponent />
</div>
</template>
<script lang="ts">
import mixins from 'vue-typed-mixins';
import SomeMixin from '~/mixins/SomeMixin';
import config from '~/utils/config';
import SomeComponent from '~/components/SomeComponent.vue'
export default mixins(SomeMixin).extend({
transition: 'scroll-x-reverse-transition',
name: 'UserManagement',
components: {
SomeComponent
},
head(): any {
const canonicalPathOptions = {
name: 'user-slug-management',
params: {
slug: String(this.user.slug ?? this.user.id)
}
}
return {
title: this.$t('this.title'),
meta: [{ hid: 'robots', name: 'robots', content: 'noindex' }],
link: [
{
rel: 'canonical',
href: config.BASE_URL + this.localePath(canonicalPathOptions),
hid: 'canonical'
},
{
rel: 'alternate',
hreflang: 'en',
content: config.BASE_URL + this.localePath(canonicalPathOptions, 'en')
},
{
rel: 'alternate',
hreflang: 'lt',
content: config.BASE_URL + this.localePath(canonicalPathOptions, 'lt')
},
// ...
]
}
}
});
Issue
The Vue component PartialComponent
has another InnerPartialComponent
that, in turn, has a Google Recaptcha (i.e. VueRecaptcha
) inside that actually is an old vue-recaptcha
(v1.3.0
). So, in general:
management.vue
> SomeComponent
>> PartialComponent
>>> InnerPartialComponent
>>>> VueRecaptcha
Due to Vue Route transition scroll-x-reverse-transition
of management.vue
that comes from Vuetify, the following happens on page change:
The captcha “jumps” on the page during the transition. Therefore, I guess, it would be appropriate to use v-show
or v-if
on the captcha VueRecaptcha
component to hide the element during route change, but am still searching for an adequate approach.
Current Workaround
Currently, the somewhat odd workaround is to dispatch a CustomEvent in beforeRouteLeave
in the “route component” and change a prop of that InnerPartialComponent
that has VueRecaptcha
inside to control its visibility like <InnerPartialComponent :is-captcha-visible="isCaptchaVisible" />
and then <VueRecaptcha v-show="isCaptchaVisible" />
inside.
<!-- File: ./mixins/SomeMixin.ts -->
import Vue from 'vue'
export default Vue.extend({
layout: 'main',
transition: 'scroll-x-transition',
// ...
beforeRouteLeave(to, from, next) {
window.dispatchEvent(new CustomEvent('before-route-leave', {
detail: { to, from }
}));
next();
}
});
<!-- File: ./components/PartialComponent.vue -->
<template>
<div>
<InnerPartialComponent v-if="isCaptchaVisible" ref="recaptcha" :site-key="reCaptchaSiteKey" />
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import InnerPartialComponent from '~/components/InnerPartialComponent.vue';
import config from '~/utils/config';
export default Vue.extend({
name: 'PartialComponent',
components: {
InnerPartialComponent
},
data() {
return {
reCaptchaSiteKey: '' as string
}
},
computed: {
isCaptchaVisible(): boolean {
return (this.reCaptchaSiteKey as string).length > 0
}
},
created() {
if (process.client) {
this.reCaptchaSiteKey = config.RECAPTCHA_SITE_KEY as string;
window.addEventListener('before-route-leave', () => {
if (process.client) {
this.reCaptchaSiteKey = '';
}
}, { once: true });
}
},
});
This works, but… it feels quite odd and unreliable. There were other ideas, including:
-
Use event @leave
of Vue built-in transition
(e.g. https://jsfiddle.net/wy9mu0cf), yet it seems the event is not passed deep enough, where component InnerPartialComponent
would not catch it.
Related answer (Fire event when changing route before DOM changes and outside the route itself?…).
-
Watch $route
inside InnerPartialComponent
, but it doesn’t work since the watcher gets removed before it actually handles the change the moment InnerPartialComponent
gets “destroyed” on page change.
Related answer (Vuejs: Event on route change…).
Question
There’s a hope this project will reach an upgrade point, but until that moment, what would be an adequate option to replace the workaround and catch the route change to hide the captcha during the transition?
Despite the route change technique, which is still very interesting, what would cause the captcha to “jump” in the first place? It seems there’s no custom CSS property that replaces the Vuetify’s styles that would modify the captcha position so much.
And… thank you very much for a suggestion in advance! ✨