Load async data into the vuex store when Nuxt app loads - vue.js

I am trialling a project in Nuxt. Liking it so far except I am a little confused as to how to load data from an external async service so that it is available in Vuex from the very first route.
I have tried adding middleware on the default layout to dispatch the store action but I do not see the service being called straight away. Only when I navigate deeper into the routes do I see the action dispatched.
I did something similar in a standard Vue project and added the created method to the App.vue.
Is there a similar way in Nuxt?

What you need is called a fetch.
The fetch method, if set, is called every time before loading the component (only for page components). It will be called server-side once (on the first request to the Nuxt app) and client-side when navigating to further routes.
Warning: You don't have access of the component instance through this inside fetch because it is called before initiating the component.
async fetch({ store }) {
await store.dispatch('your-action')
}
If you need parameter:
async fetch({ store, params }) {
await store.dispatch('your-action', params.id)
}
I gave an example of id. The name of the parameter depends on the name of your page.
_id => params.id
_slug => parmas.slug
...

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.

Vue - check localStorage before initial route (for token)

I have a Vue app which does a little localStorage and server check on app load, to determine where to initially route the user.
This is in the App's main entry component, in the created() hook
My problem is that the default / route's Component visibly loads first, then the server call and everything happens which causes the user the route to their correct location
How can I delay the rendering of the initial component until my app's main component created() method completes, and then purposely navigates the user to the correct route?
I had this problem before and I firmly believe that you must have the initial files for your routes and your router configuration.
In the configuration, you could handle the permission and router before each route and with next() . In the router file, you can set your params and check them in the index.js file(router configuration)
you could also use your localStorage data in Router.beforeeach
EDIT: I just saw you used the created method... like mentioned below use beforeRouteEnter instead with the next() parameter it provides
First of all I wouldn't recommend using a delay but instead a variable that keeps track if the API call is done or not. You can achieve this using the mounted method:
data() {
return {
loaded: false,
}
}
async mounted() {
await yourAPICALL()
if (checkIfTokenIsOkay) {
return this.loaded = true;
}
// do something here when token is false
}
Now in your html only show it when loaded it true:
<div v-if="loaded">
// html
</div>
An better approuch is using the beforeRouteEnter method which allows you to not even load the page instead of not showing it: https://router.vuejs.org/guide/advanced/navigation-guards.html

Fetching global api data in nuxt

I'm starting Nuxt project and I have to fetch basic data from my backend.
for example language dictionary or some data for leftbar and etc.
in Vue I can just call everything I need in App.vue and save it in vuex and then use whenever I want, but with Nuxt every page is different and I'm curious where should i call this global data api calls.
I'm guessing I have to create middleware and call actions if data is not loaded already or there is better solution?
You mention it's global data and you're using universal mode- I think you're looking for nuxtServerInit(). This store action runs once on the server, before created, mounted hooks etc. You can use it to populate your store with data that your components (including pages) rely on.
Take a look at the docs.
actions: {
nuxtServerInit ({ commit }, { req }) {
if (req.session.user) {
commit('user', req.session.user)
}
}
}

Share code in asyncData and mounted using Nuxt

I want to share same code block to get data in asyncData and mounted using Nuxt.
For example in asyncData
async asyncData({ $api, $auth, route, error, store }) {
if( !process.server ) return null;
let res = {};
let stockData = await $api.stocks.getStock(route.params.name);
if( stockData ) { res.stockData = stockData.data; }
return res;
},
And in mounted()
if(ObjectHelper().isEmpty( this.stockData )) {
this.$api.stocks.getStock(route.params.name).then(res => {
this.stockData = res.data;
})
}
Those two code blocks are all getting data from server-side. How to write a common function to reuse it but not write twice? In nuxt documents, You do NOT have access of the component instance through this inside asyncData because it is called before initiating the component.
Regarding your comment to have the API call server and client-side, which is the reason for your question - it is not necessary to duplicate or share it within mounted as asyncDatagets called server and client-side. You will find following in the nuxt docs:
asyncData is called every time before loading the page component. It
will be called server-side once (on the first request to the Nuxt app)
and client-side when navigating to further routes.
That means - lets say you have your asyncData on page A and a user enters your site using page B and navigates client-side via a nuxt-link from page B to page A it will fire asyncData client-side too before initializing the page component.