How to fetch data before mounting the root Vue instance? - vue.js

TL;DR: What is a good way to do async stuff before starting the app?
vue-devtools does not work correctly if I wait for async stuff to finish before creating / mounting the root Vue instance.
I'm new to Vue.js and I'm developping a back office app where nothing is accessible unless the user is logged. Except for the sign in / sign up pages of course.
So on page load I do something like this:
// Set up store and router ...
router.beforeEach(function (to, from, next) {
/* redirect to auth if store.state.auth.isLogged is false */
}
// Dispatch an action that send a request to the API to get the connected user
// (or null if the user is not logged)
store.dispatch('getAuth').then(user) {
// Mount the app one the request is done and the mutation for isLogged is done
new Vue({
el: '#app',
router,
store,
// ...
});
}
And in my index.html I have a pure HTML/CSS loading page that waits for the vue app to mount.
So this works fine, on load, the app check if the user is logged and once this is done, it redirects to the auth pag if needed.
My problem is mainly with vue-devtools, it seems that if the root Vue instance is not mounted on page load, I can't inspect components from the vue-devtools, but the vuex & events inspection works. Plus the extension icon on chrome is grayed out ("Vue.js not detected") whereas it kind of works.
Am I doing something wrong? Or is it a problem with the devtools?

What you are doing is fine. There is a refresh button at the top right of the Vue dev tools. Click it and your asynchronously loaded Vue instance should be detected.

Related

Is there anyway to ignore the *failure: Avoided redundant navigation to current location* error using Vue Router and just refresh the page?

I see this question has been asked a few times on here, but none of the answers have really helped me in this current situation.
I have an app I'm working on with a sidebar with tabs that link to different dashboards. Each of the SidebarLinks are a router-link with the to key being fed the route prop from the main component.
Inside one of these dashboards, the Analysis dashboard, there is another router that routes you to child routes for specific Analyses with their own ids (EX: /analysis/1).
The user clicks on a button for a specific analysis and they are routed to a page containing that information, on the same page.
The Error
When I click the Analysis SidebarLink the route in the url changes back to /analysis, but the page doesn't update/refresh.
I don't get an error in the console, but I do get the failure in the devtools.
I understand that Vue Router doesn't route back to a route you are already on, but I need it to. If you refresh the page when the url is just /analysis it routes back to it's inital state.
Is there anyway to refresh when it rereoutes to /analysis? Or a way to handle this error to work as intended?
What I've tried
I've tried changing the router-link to an <a> tag and programatically use router.push and then catch the error, but that doesn't do anything.
I've tried checking if the route.fullPath.contains("/analysis") and then just do router.back() but that doesn't seem to work either.
SidebarLink router function
function goToRoute() {
console.log(`route.fullPath → `, route.fullPath)
if (route.fullPath.match('/analysis*') as any) {
console.log('route includes /analysis')
router.back()
} else {
console.log('route doesnt inclue /analysis')
router
.push({
path: props.route,
})
.catch(() => {})
}
}
Inital /analysis Page
This is what the page looks like normally
/analysis/1 Page
This is what the route to analysis/1 looks like (url changes)
/analysis/1 Page When Issue Analysis SidebarLink Clicked
This is what the route to analysis looks like when the sidebarlink is clicked (url changes, but the page stays the same)
I suspect you are fetching your data from a backend service or data files
If yes you can refetch the data everytime the route param changed by watching it.
watch: {
'$route.params.id': function (id) {
if(id)
this.$store.dispatch('fetchOneAnalys', id)
else
this.$store.dispatch('fetchAllAnalyses')
}

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

How to refer other html file to load in production mode of application that are developed using electron?

I have problem in building electron application, which is made using electron.js and vue.js.
The development is almost done, so I need to build it and make production.
Basically, my project has license system, so when the program starts, users should activate it by entering license key. I wrote this part inside activate.html. So, inside public folder, there are two html files, one is default index.html, and the other is activate.html
Inside activate.html, when activation is success, startMain() javascript function is called. Inside this function, the program is navigated to main program, especially to index.html. This html file is Vue application, and then main program is started.
All this process is working fine in the development mode. But when it
comes to production, this is not working anymore. I probably know why this
happens, this is just because index.html is not available in
production mode when all these are built up.
Alternatively, I tried to load localhost:8080 inside
startMain(). But also this works fine in development mode, but unluckily, we don't have localhost server on production mode, so this
doesn't solve the problem.
This is startMain() inside activate.html
function startMain() {
window.__static = "index.html";
// Alternatively, we can use location.href on development mode.
// window.location.href = "http://localhost:8080/";
}
This is background.js ( electron main engine)
function createWindow() {
// Create the browser window.
win = new BrowserWindow({
width: 1400,
height: 900,
titleBarStyle: "hiddenInset"
});
win.setMenuBarVisibility(false);
if (isDevelopment) {
// Load the url of the dev server if in development mode
// win.loadURL(process.env.WEBPACK_DEV_SERVER_URL);
win.loadURL(`file://${process.cwd()}/public/activate.html`);
// if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol("app");
// Load the index.html when not in development
win.loadFile("activate.html");
}
win.on("closed", () => {
win = null;
});
}
What I want is to make it run correctly in production mode.
Specifically, when the user finishes activation flow ( activate.html),
it should start main program ( index.html )
Please help me with this issue. I welcome any comments.
I built this application using vue-cli-plugin-electron-builder package
In Vue usually you use vue-router to switch between views. You set the BrowserWindow.location the first time, in background.js, but after that you are in the Vue app and you make router calls like this:
router.replace({ name: "index" }).catch(() => { });
It's hard to guess what to do in your case, without seeing the rest of your activate.html. You need to define router in your App and probably want to convert your activate.html into an activate.vue component and make it the proper first page of your App.
I think the cli-plugin boilerplate will call main.js where you will define your app and bootstraps it. You add router there:
import router from "./router";
window.$app = new Vue({
router,
In your bootstrap function you can also call something like router.replace({ name: "activate"}) to start your app with your activate vue component.

Nuxt JS LocalStorage in Universal mode not working

I'm working on a Nuxt JS application which utilises LocalStorage. When compiling in SPA mode it functions correctly, however I need to switch my project to universal mode to get my SEO meta titles and descriptions to work when generating my project, after switching I get an error on a few pages which utilise LocalStorage:
localStorage is not defined
Has anyone got any suggestions to fix this?
You don't have localStorage because there is no such thing in Node.js environment. You also don't have window and document objects.
https://ssr.vuejs.org/guide/universal.html#access-to-platform-specific-apis
Nuxt.js uses Vue SSR under the hood.
However, you still have a store (Vuex). And it will be synchronized between node and browser.
I needed data to persist between sessions for GDPR. You can use the mounted life cycle event and wait for window.localStorage to be available. Then assign it to a data property and add a v-if in a wrapper tag so the page doesn't start rendering before localeStorage is available. I'm doing this with a static nuxt build:
<div v-if="localStorageReady">
Awesome stuff here...
</div>
data() {
return {
localStorageReady: false,
}
},
mounted() {
if (window.localStorage) {
this.localStorageReady = true
}
}

Vue router reloading the current route

Without reloading the whole page I need to reload the current route again (Only a component reload) in a vue app.
I am having a path in vue router like below,
{
path: "/dashboard",
name: "dashboard",
component: loadView("Dashboard"),
},
When user clicks on the Dashboard navigation item user will be redirected to the Dashboard page with vue router programmatic navigation
this.$router.push({ name: "dashboard" });
But when user already in the dashboard route and user clicks the Dashboard nav item again nothing happens. I think this is vue router's default behaviour. But I need to force reload the Dashboard component (Not to refresh the whole page).
I can't use beforeRouteUpdate since the router is not updated. Also I have tried the global before guards like beforeEach. But it is also not working.
How can I force reload the dashboard component without reloading the whole page?
It can be done in two ways.
1) Try doing vm.$forceUpdate(); as suggested here.
2) You can take the strategy of assigning keys to children, but whenever you want to re-render a component, you just update the key.
<template>
<component-to-re-render :key="componentKey" />
</template>
<script>
export default {
data() {
return {
componentKey: 0,
};
},
methods: {
forceRerender() {
this.componentKey += 1;
}
}
}
</script>
Every time that forceRerender is called, the prop componentKey will change. When this happens, Vue will know that it has to destroy the component and create a new one.
What you get is a child component that will re-initialize itself and “reset” its state.
Not mentioned here, but as the offered solutions require a lot of additional work just to get the app to render correctly, which imo is a brittle solution.. we have just implemented another solution which works quite well..
Although it is a total hack.
if (this.$route.name === redirect.name) {
// this is a filthy hack - the vue router will not reload the current page and then have vue update the view.
// This hack routes to a generic page, then after this has happened the real redirect can happen
// It happens on most devices too fast to be noticed by the human eye, and in addition does not do a window
// redirect which breaks the mobile apps.
await this.$router.push({
name: RouteNames.ROUTE_REDIRECT_PLACEHOLDER
});
}
... now continue to do your normal redirect.
Essentially, redirect to a placeholder, await the response but then immediately continue to another page you actually wanted to move toward