nuxt route change query endless loop - vue.js

I have a nuxt one-pager website and I am trying to set up a router for the language switcher. My headless CMS (Storyblok) has language logic setup by using ?language=xyz.
The data is fetched by using AsyncData() hook in the pages/index.vue.
Now when I change the route from '/' to '/xyz' the AsyncData() is called again and the route changes.
When I do not change the path but only the query (language=xyz) this does not happen.
I tried to add the following:
beforeRouteUpdate (to, from, next) {
console.log('BEFORE UPDATE', to.query.language);
console.log('BEFORE UPDATE 2', this.$route.query.language);
if(to.query.language != this.$route.query.language) {
//this.$router.push({ path: '/', query: { language: to.query.language } })
next();
}
}
When I now click on the navigation I can see in the console log, the current and new language but still nothing happens.
When I try to push the route (commented out code above) I get an endless loop and see the beforeRouteUpdate running again and again, but the AsyncData is not run.
What can I do, so the behavior is the same as when the path of the route changes?

Are you sure that you are actually fetching it again from storyblok? Are you fetching it with the storyblok module or are you fetching it straight from the API manually?
What I would suggest as a counter option would be to add the nuxt i18n module and fetch the data from storyblok with the i18n language and use the i18n language switch feature! That way you can also add translations that don't fit into the storyblok structure.

Related

How to render a page content with dynamic routing on page reload?

I have a SPA with dynamic routing. On page loading I'm making a fetch request for products. Then set it to the vuex store and use it. I also have a dynamic routing for product pages '/product/:id'
The problem is if I reload a product page say site.com/product/2 then nothing renders. I think this happens because at that moment store is empty and there're nothing to render.
How can I fix that?
In ProductPage component I tried using navigation guard with no success. Code below returns error: Cannot read properties of undefined (reading 'products').
Fetch request is made on mounting in TheMain component (when I reload ProductPage fetch request is made as well).
ProductPage component:
computed: {
...mapState(["products"]), // get products array from store
},
beforeRouteEnter(to, from, next) {
if (this.products) { // check if products array exists and then load data
next(true);
}
},
I think you need to make beforeRouteEnter async and add await for the store action which does the fetch or axios call.
async beforeRouteEnter(to, from, next) {
if (this.products) {
// check if products array exists and then load data
await dispatch(‘action_to_get_data’);
/// Wait to go to next for dispatch to finish.
next(true);
}
}
In your action you also need to use async / await.
What also might help is a v-if on the ProductPage component, so the components only loads when the products are loaded in the store.
For vue3 you may also take a look at the experimental Suspense component. But I don’t know if this works with beforeRouteEnter.
Hope this helps.

Initate Session on page load

I have a requirement where I need to set Global Axios header and refresh session(Calling multiple Api's). In vuejs we would have a App.vue which is the entry point for our application we would do these types of processing in that.
App.vue
export default {
async created(){
await this.$store.dispatch('initateSession');
}
}
The benefits of doing this is that this is called only once(not on every route change), and on every page load, I am looking to implement the same functionlity in Nuxt.
Now as we dont have any App.vue in case of Nuxt one solution that i could think off is to create a middleware for this , which can work but that will be called on every route change I dont want that, does anyone know any workaround for it?
Instead of middleware you can create a plugin:
plugins/session.js:
window.onNuxtReady(() => {
// Do something
})
And then add it to the plugins section inside nuxt.config.js.

Pagination is not working as expected with vue router

I am pushing query parameter for page change it works fine:
getProducts(){
this.$router
.push({
name: 'products',
query: {
page: this.page,
},
})
.catch(() => {})
... fetching data from backend
}
It works fine when I just click pagination items and data is loading correctly but when I click back from browser query param is geting changed but pagination and data doesn't can't changed because this.page value remains the same. How can be this fixed?
You will have to add a befoureRouteUpdate(to, from, next) hook in your component - or a watcher on $route. Vue-Router reuses the same component because you are not changing the route - only the query parameters.
Keep in mind that:
the beforeRouteUpdate will be called only when the component is being reused - not when the route is visited for the first time (for the latter you need beforeRouteEnter)
the watcher will be also called when you leave the route (e.g. if you go to clients page)

Recommended way of waiting on an Apollo query before rendering the next page?

When using the Apollo module in a Nuxt app, the default behavior when changing routes is to render the new page immediately, before data has been fetched via Apollo.
This results in some pretty janky rendering experiences where the page does a partial render and very soon after completes rendering with data from the server, making everything on the page shift due to the changing size of components that now have data. This looks pretty bad because the data actually comes back fairly quickly, so it would be fine to wait for the data to return before rendering the new route.
What's the recommended way of waiting on the Apollo queries on a page (and its subcomponents) to complete before rendering the page?
(There's a related question that's not specific to Nuxt, but I'm not sure how to translate the recommendation to a Nuxt app.)
I'd love to see a code example of using beforeRouteEnter to fetch data via Apollo and only entering the route once the data is fetched.
Haven't used this module before, but it should be like any other async action you want to perform before page rendering in Nuxt.
It only depends if you want to pre-fill the store:
https://github.com/nuxt-community/apollo-module#nuxtserverinit
https://nuxtjs.org/guide/vuex-store/#the-nuxtserverinit-action
or only one page:
https://github.com/nuxt-community/apollo-module#asyncdatafetch-method-of-page-component
https://nuxtjs.org/guide/async-data
You can use async/await or promises if you have more than one request before page should be rendered.
When async actions are finished, Nuxt starts rendering the page. This works for SSR and if you navigate to pages on the client (nuxtServerInit will only fire once when real request is made, not when navigating on client side).
Side note: beforeRouteEnter is usually used, to validate params and check if the route is allowed.
did you try disabling the prefetch?
prefetch: false
The best approach is to use the loading attribute:
<template>
<div v-if="!this.$apollo.loading">
Your product: {{product}}
</div>
</template>
<script>
export default {
name: "Product",
apollo: {
product: {
query: productQuery,
variables() {
return {
productId: this.productId
}
}
}
}
}
</script>
I'm unfamiliar with Apollo, but I think this is what you are looking for:
// Router.js
beforeRouteEnter(to, from, next)
{
executeSomeApolloPromise().then((data) => {
// The promise has now been complete; continue to the component.
next((vm) => {
// You have access here to the component instance via `vm`.
// Note that `beforeRouteEnter` is the only guard that has this.
vm.someApolloData = data;
});
});
}
See https://router.vuejs.org/guide/advanced/navigation-guards.html#per-route-guard

Navigate with Vue router-link changing only query parameters, not path

In Vue.js, I'm using <router-link> to navigate, as such:
<router-link :to="{ path: '/', query: { q: item.id, lang: lang } }">{{item.name}}</router-link>
This does not update the path, only the query string. Although the resulting URL is formatted correctly, it does not trigger navigation in Vue, apparently because the path has not changed, only the query string.
If I put a beforeRouteUpdate hook on my component, I can see that the new query parameters appear in the "to" object.
How can I make Vue perform the navigation, even though only the query parameters have changed?
You have most probably solved this already, so this answer could be useful for others with this problem.
Documentation link - In-Component Guards - beforeRouteUpdate
If I put a beforeRouteUpdate hook on my component, I can see that the new query parameters appear in the "to" object
This means you doing it all correctly since you are seeing the new parameters in the component guard, So next, what you need to do is to do as prescribed below in docs and example
Fetching After Navigation
So all you now have to do is fetch your new data using these new parameters
beforeRouteUpdate (to, from, next) {
this.post = null
// replace `getItem` with your data fetching util / API wrapper
getItem(to.params.q, to.params.lang, (err, post) => {
this.post = post
next()
})
}