How to get access to template in asyncData? - vue.js

I want to get access to this.$el in asyncData.
I use a database to store translations.
I want to get a list of translations that are used on the current page.
Then I will send a request to the server to receive them.
After that, I will merge it.
i18.mergeLocaleMessage( locale, message )
How to do it ?

You can access i18n with something like this, no need to access the template for this use case
asyncData ({ app }) {
console.log(app.i18n.t('Hello'))
}
Looking at the lifecycle of Nuxt, asyncData will happen before any template is generated at all, so it is impossible with asyncData.
And even if it was possible with some hacky trick, it would be a bit strange to have to look inside of your template to then have some logic for i18n fetching.
Why not getting a computed nested object and loop on this through your template, after you have fetched all of your required translations ?
Also, you're using asyncData + an API call each time ? So, for every page: you will stop the user, let him wait for the API call and then proceed ?
Latest point, if you are on your page and you hit F5, then asyncData hook will not be triggered. Just to let you know about this caveat.
Alternative solutions:
using the fetch() hook and display a loader until you have fetched all your translations, still better to not rely on the content of the template. This will work even on F5 and can produce a more smooth experience (by not blocking the navigation).
getting your i18n whole translations globally, at some point when your user's connection is idle. Rather than on per-page. Especially because you will need to handle the logic of not fetching some translations that you already have (if the user visits a page twice).

Related

asyncData hook when hard refreshing in Nuxt

I just realized that the asyncData hook is not called when hard refreshing the page. But I have important data to load to show on that page. And I want to make sure that they are always available, even when the user hard refreshes the page.
asyncData from the documentation
the promise returned by the asyncData hook is resolved during route transition
In that case, the best way is to use the fetch() hook and display a loader while you do finish your calls thanks to the $fetchState.pending helper.
Actually, I do think that fetch() is better in many ways.
Quick article on the subject: https://nuxtjs.org/blog/understanding-how-fetch-works-in-nuxt-2-12/
The one on the bottom of the page (Sergey's) is cool with some practical example of a nice implementation too.
You could also use this somewhat hacky solution in a layout to see if the initial location (the one you are when you hard refresh) is the one you want to be. Basically, if you land on a specific page (hard refresh or following a new window page for example) but want to have some custom behavior.
beforeCreate() {
if (!['some-other-page'].includes(this.$router.history._startLocation)) {
this.$router.replace({ name: 'index' }).catch(() => {})
}
},
Still, this one infinite loops if used in a middleware (or I made a mistake) and it's less clean than a fetch() hook.

How to avoid fetching data for page that has just been navigated away from when back button is pressed in vue spa

I have a vue spa app. I fetch data for the page in the created hook
My question is how can i avoid fetching data again for the page that has been previously navigated away from if back button is pressed
EDIT
my app is in vue 3 I later used < keep-alive > which worked but am unable to clear the cache even when using max prop on the < keep-alive :max="2" > component
Quite simply, by caching it.
There are several ways to implement some kind of caching, but it largely depends on your current setup.
If you are not using vue-router and the back button causes your page to re-render, you will need to persist the data some other way, such as by using localStore. If you're using vuex, and you want to persis the data, you can try vuex-persistedstate. I'm assuming though that you are not looking to persist the data and the page is not re-loading on redirect.
If you are already using vuex, you can cache the data in the store.
Another option is to use the keep-alive component
If you don't want to rely on a 3rd party tool, you can also wrap your API call with a caching function.
Here is what that may look like. When the data is loaded, it gets cached, and when the cache exists, the cached result is returned as a promise, so that the method returns the same type either way.
let getMyDataCache = null;
export const getMyData(){
if (getMyDataCache) return new Promise((resolve)=>resolve(getMyDataCache));
return fetch('http://example.com/movies.json')
.then(response => response.json())
.then(data => {
getMyDataCache = data;
return getMyDataCache;
});
}
You could use vuex to store the state for your page - then in your created() hook, check if the state has been populated before fetching the data.
To clarify (since this answer is getting downvoted and I'm not sure why): you need to store the fact that you have already fetched your data somewhere. You can't do it inside your component since it will be remounted when you return to the page. If you have a state management pattern in place (e.g. with Vuex), you can store your data there so that when the component is remounted, you can check your store to see if the data has already been fetched before you try to fetch it again.
If you are using vue-router, the answers on this thread might help you instead: How to make certain component "keep-alive" with router-view in Vue 3?

How To Ensure Reference Data Is Loaded In Vue?

I have webpack setup to bundle all of the source. I have a Vue object that is the page and multiple Vue components of my own creation. This works great!
I am now getting reference data from the database to fill in certain default options for some of these components. In my pages Mounted or Created events (there is no difference for my question) I am calling a method that will check to see if the data exists in localStorage and if not, it will extract the data from the database.
Once Extracted, I have it in localStorage so it is not an issue. However, the first time I need to gather the data (or when I need to refresh it because I have another trigger that lets me know when it has changed) the page and components have rendered (with errors because of lack of data) before the data comes back. The fetch method is in a promise, but mounted events don't seem to care if a promise exists within in before it continues to the next component.
So what is the best practice for loading/refreshing reference data in Vue? I am currently not using VueX because this is not a SPA. Sure, it is a single page that is doing things (there are many single pages that do their own thing in this site) but I have no need to make it a full SPA here. But If VueX and its store will give me some sort of guarantee that it will occur first or page/components will run AFTER VueX things, I will learn it.
Have you tried doing so:
<component v-if="page.isDataLoaded">...</component>
in your Vue-component:
data() {
return {
page: {
isDataLoaded: false,
}
}
},
mounted() {
this.fetchPageData().then(() => this.page.isDataLoaded = true);
}
You can use v-if and v-else to show, for example page loader element like so:
<PageLoader v-if="!page.isDataLoaded"></PageLoader>
<component v-else>...</component>

what is the best way to use vue, vue-router to make a dashboard with keep-alive?

I'm using vue2, vue-router with keep-alive to develop a dashboard. What I want to achieve is following:
page should re-fetch when login -> logout -> login other account
(use beforeRouterEnter, beforeRouterLeave to re-fetch !? or use condition to keep alive component or not !?)
but vue-router can't use vue data to check page should keep alive or not...
in HTML template, it will render and render. If there has some vuex data show directly in HTML, it will error with not have the object inside object yet ex:
user: {
plan: {
xxx: ''
}
}
sure I can give vuex data all of property init data. It can avoid this thing happen, but if the page with keep alive, it still happens...
also, I can write many v-if in the template, but I think it's not a good idea to put v-if all around the template to make sure data exist..ex:
v-if="user.plan && user.plan.xxx"
or add v-if in component root template, is that a good idea !?
use beforeMount without keeping alive, there is no problem but it's not a good user experience dashboard (when you change page, data disappear or you have already load page data but load again)
Is there anyone who really use Vue 2 and it's relative library (vuex, vue-router) to develop can tell me how you develop your SPA project !?

React server/client rendering

I'm currently developing an application with express, mongoose and react.
Server rendering:
Get data from my local API (stored in mongoose)
React.renderToString component with data from API as props
This works fine with a static UI. However, now I would like to have some interaction in the UI. Therefore I have to render the same component with the same props on the client side again. Now I'm struggling around about the best way to get my mongoose data (props) on client side... Is it really necessary to save all my json data from mongoose in a script tag and then read it out like this?
if (typeof window !== 'undefined') {
var props = JSON.parse(document.getElementById('props').innerHTML);
React.render(MyComponent(props), document.getElementById('reactMarkup'));
}
Moreover, another way would be to make an ajax call in the componentDidMount function. Which way would you prefer? It would be great if you could help me doing that without the script tag or ajax call... :)
If you want React to transparently upgrade to an SPA on the client side, you need to construct your top-level component with the same props on the client as you did on the server. The most common approach is the one you already mentioned: serialize the props you used and pass them to the host page in a script block or similar.
If you do the Ajax request in componentDidMount, you'll render once with no properties, overwriting the prerendered HTML, and then rerender with the correct properties—I would probably avoid this technique.