I have a vue-router like this
import Vue from 'vue';
import Router from 'vue-router';
import http from './helpers/http';
import Home from './views/Home/Home.vue';
import HomeMentor from './views/Home/HomeMentor.vue';
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: '',
component: () => import(/* webpackChunkName: "Container" */ './components/Container.vue'),
children: [
{
path: '/',
name: 'Dashboard',
component: {
render(c) {
http.request('GET', '/profile').then( async ({ data }) => {
console.log(data.profile.email)
if (data.profile.email === "vickysultan08#gmail.com") {
console.log('sip')
return c(HomeMentor);
} else {
return c(Home);
}
});
}
},
}
],
beforeEnter: isAuthentication,
}
});
The thing is, only the return component inside the conditional statement that cannot executed inside axios statement as the result below
While the return component inside the conditonal statement can be executed outside the axios statement like this
children: [
{
path: '/',
name: 'Dashboard',
component: {
render(c) {
a = 10
if (a === 10) {
console.log('sip')
return c(HomeMentor);
} else {
return c(Home);
}
}
},
}
],
I'm quite new in Vue JS and have to continue other person's code. Any advice?
Unfortunately, render functions must be synchronous.
What you may be able to do instead is simply use an async function to return the component, ala Async Components and Lazy Loading Routes.
const Dashboard = () => http.request('GET', '/profile').then(({ data }) => {
console.log('profile email', data.profile.email)
let isMentor = data.profile.email === 'vickysultan08#gmail.com'
let componentPath = `./views/Home/${isMentor ? 'HomeMentor' : 'Home'}.vue`
return import(componentPath) // chains in the "import" promise
})
and then in your route...
component: Dashboard,
If lazy-loading the component isn't working for you, you could always try pre-loading it
import http from './helpers/http';
import Home from './views/Home/Home.vue';
import HomeMentor from './views/Home/HomeMentor.vue';
const Dashboard = () => http.request('GET', '/profile').then(({ data }) => {
let isMentor = data.profile.email === 'vickysultan08#gmail.com'
return isMentor ? HomeMentor : Home
})
Related
Before accessing any page, except login and register; I want to authenticate the user with Navigation Guards.
Following you can see my code for the vue-router. The "here" gets logged, but the navigation is not cancelled in the line afterwards. It is still possible that if the user is not authenticated that he can access the /me-route
my router-file:
import { createRouter, createWebHistory } from "vue-router";
import axios from "axios";
import HomeView from "../views/HomeView.vue";
import RegisterView from "../views/RegisterView.vue";
import LoginView from "../views/LoginView.vue";
import MeHomeView from "../views/MeHomeView.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
name: "home",
component: HomeView,
},
{
path: "/register",
name: "register",
component: RegisterView,
},
{
path: "/login",
name: "login",
component: LoginView,
},
{
path: "/me",
name: "me",
component: MeHomeView,
},
],
});
router.beforeEach((to, from) => {
if(to.name !== 'login' && to.name !== 'register') {
console.log(to.name);
axios.post("http://localhost:4000/authenticate/", {accessToken: localStorage.getItem("accessToken")})
.then(message => {
console.log(message.data.code);
if(message.data.code === 200) {
} else {
console.log("here");
return false;
}
})
.catch(error => {
console.log(error);
return false;
})
}
})
export default router;
Navigation guards support promises in Vue Router 4. The problem is that promise chain is broken, and return false doesn't affect anything. As a rule of thumb, each promise needs to be chained.
It should be:
return axios.post(...)
The same function can be written in more readable and less error-prone way with async..await.
I have a Vue SPA with i18n and some views that require authentication via navigation guard.
When i am not authenticated and go to url via my browser:
examplepage.com/en/lockedpage
i get redirected to:
examplepage.com/en/login
which is good, however when i click a button that runs:
#click="$router.push(`/${$i18n.locale}/lockedpage`)"
i get in to the page even if i am not authenticated.
I want to get redirected to the login page if not authenticated
this is my router.js:
import Vue from 'vue';
import Router from 'vue-router';
import Home2 from './views/Home2.vue';
import Login from './views/Login.vue';
import Register from './views/Register.vue';
import ErrorLanding from './views/ErrorLanding.vue'
import Root from "./Root"
import i18n, { loadLocaleMessagesAsync } from "#/i18n"
import {
setDocumentLang
} from "#/util/i18n/document"
Vue.use(Router);
const { locale } = i18n
export const router = new Router({
mode: 'history',
base: '/',
routes: [
{
path: '/',
redirect: locale
},
{
path: "/:locale",
component: Root,
children: [
{
name: 'Home',
path: '',
component: Home2,
},
{
name: 'Home2',
path: '/',
component: Home2,
},
{
name: 'Login',
path: 'login',
component: Login,
},
{
path: 'register',
component: Register,
},
{
path: 'lockedpage',
name: 'lockedpage',
webpackChunkName: "lockedpage",
meta: {authRequired: true},
component: () => import('./views/LockedPage.vue')
},
{
path: '*',
component: ErrorLanding,
name: 'NotFound'
}
]
}
],
router.beforeEach((to, from, next) => {
if (to.params.locale === from.params.locale) {
next()
return
}
const { locale } = to.params
loadLocaleMessagesAsync(locale).then(() => {
setDocumentLang(locale)
const publicPages = [ `/`, `/${locale}`, `/${locale}/`];
const authRequired = !publicPages.includes(to.path);
const loggedIn = localStorage.getItem('user');
const redirect = to.path;
if (authRequired && loggedIn === null) {
if(to.meta.authRequired === false) {
next();
}
else
next({ name: 'Login', query: { redirect: redirect } });
} else {
next();
}
})
next()
});
Why does my navigationguard skip when using router.push() ?
This issue started after adding i18n, with localeredirect. so all routes comes after a locale for example: /en/.../
As Estus points out in the comments, the issue is that the first thing you're checking for is if the locales match, and if they do you're calling next() and sending the user to the page.
As outlined here:
Make sure that the next function is called exactly once in any given pass through the navigation guard. It can appear more than once, but only if the logical paths have no overlap, otherwise the hook will never be resolved or produce errors.
If you need to keep the locale check between the to and from pages, you could do something like:
if (to.params.locale === from.params.locale && loggedIn) {
next()
return
}
which will check if the loggedIn variable is truthy before pushing the user to the page they're trying to navigate to.
I believe just removing the if statement that checks if the locales match would also work.
I would like to check in my Vuex store whether a user has the 'admin' role before entering the /dashboard route. But I can't properly access data from store.getters.
I use Quasar (Vue.js) and Vuex + Typescript.
In the routes.ts file, on the beforeEnter() function, I can access getters from the store with a console.log(store.myStore.getters). Here I see userInfos inside:
I don't understand why I only get {} and not {...} (Note that if I click on it, I see its contents).
But if I call console.log(store.myStore.getters.userInfos), I don't see the data:
Here is index.ts (router):
import { route } from 'quasar/wrappers'
import VueRouter from 'vue-router'
import { Store } from 'vuex'
import { StateInterface } from '../store'
import routes from './routes'
export default route<Store<StateInterface>>(function ({ Vue }) {
Vue.use(VueRouter)
const Router = new VueRouter({
scrollBehavior: () => ({ x: 0, y: 0 }),
routes,
mode: process.env.VUE_ROUTER_MODE,
base: process.env.VUE_ROUTER_BASE
})
return Router
})
Here is routes.ts (router):
import { RouteConfig } from 'vue-router'
const routes: RouteConfig[] = [
{
path: '/',
component: () => import('layouts/Login.vue'),
children: [
{ path: '', component: () => import('pages/Index.vue') },
{ path: '/inscription', component: () => import('pages/SignUp.vue') },
{ path: '/connexion', component: () => import('pages/SignInPage.vue') }
]
},
{
path: '/main',
component: () => import('layouts/MainLayout.vue'),
children: [
{ path: '', component: () => import('pages/Index.vue') },
{ path: '/dashboard', component: () => import('pages/DashboardB2B.vue'),
beforeEnter: (to, from, next) => {
const store = require('../store')
console.log("before enter")
console.log(store.myStore.getters)
return next();
}, },
{
path: '/ajouter-un-referentiel',
component: () => import('pages/ReferentielMetier.vue')
},
{
path: '/init',
component: () => import('components/bot/BotSkeleton.vue')
}
]
},
{
path: '/bot',
component: () => import('layouts/Bot.vue'),
children: [
{
path: '/ajouter-un-referentiel',
component: () => import('pages/ReferentielMetier.vue')
},
{
path: '/init',
component: () => import('components/bot/BotSkeleton.vue')
}
]
},
// Always leave this as last one,
// but you can also remove it
{
path: '*',
component: () => import('pages/Error404.vue')
}
]
export default routes
And here is index.ts with the store (Vuex):
import Vue from 'vue'
import { store } from 'quasar/wrappers'
import Vuex from 'vuex'
Vue.use(Vuex)
import matching from './modules/matching'
import orga from './modules/organigrame'
import user from './modules/user'
export interface StateInterface {
example: unknown
}
let myStore: any
export default store(function({ Vue }) {
Vue.use(Vuex)
const Store = new Vuex.Store<StateInterface>({
modules: {
matching,
orga,
user
},
// enable strict mode (adds overhead!)
// for dev mode only
strict: !!process.env.DEBUGGING
})
myStore = Store
return Store
})
export {myStore}
EDIT: Looks like my console.log runs before the getters are loaded, because when I check out Vue developer tools, I see everything. How can I check the store if the store itself doesn't load before the beforeEnter function?
please try like this
store.myStore.getters["userInfos"]
I'm having exact issue, even if i use async/await :S
try this
router.beforeEach(async(to, from, next) => {
const userInfo = await store.getters.userInfos;
console.log(userInfo);
});
Context: I am trying to get place data via the place_id on the beforeEnter() route guard. Essentially, I want the data to load when someone enters the url exactly www.example.com/place/{place_id}. Currently, everything works directly when I use my autocomplete input and then enter the route but it does not work when I directly access the url from a fresh tab. I believe the issue is because google has not been created yet.
Question: How can I access PlacesService() using the beforeEnter() route guard ?
Error: Uncaught (in promise) ReferenceError: google is not defined
Example Code:
In one of my store modules:
const module = {
state: {
selectedPlace: {}
},
actions: {
fetchPlace ({ commit }, params) {
return new Promise((resolve) => {
let request = {
placeId: params,
fields: ['name', 'rating', 'formatted_phone_number', 'geometry', 'place_id', 'website', 'review', 'user_ratings_total', 'photo', 'vicinity', 'price_level']
}
let service = new google.maps.places.PlacesService(document.createElement('div'))
service.getDetails(request, function (place, status) {
if (status === 'OK') {
commit('SET_SELECTION', place)
resolve()
}
})
})
},
},
mutations: {
SET_SELECTION: (state, selection) => {
state.selectedPlace = selection
}
}
}
export default module
In my store.js:
import Vue from 'vue'
import Vuex from 'vuex'
import placeModule from './modules/place-module'
import * as VueGoogleMaps from 'vue2-google-maps'
Vue.use(Vuex)
// gmaps
Vue.use(VueGoogleMaps, {
load: {
key: process.env.VUE_APP_GMAP_KEY,
libraries: 'geometry,drawing,places'
}
})
export default new Vuex.Store({
modules: {
placeModule: placeModule
}
})
in my router:
import store from '../state/store'
export default [
{
path: '/',
name: 'Home',
components: {
default: () => import('#/components/Home/HomeDefault.vue')
}
},
{
path: '/place/:id',
name: 'PlaceProfile',
components: {
default: () => import('#/components/PlaceProfile/PlaceProfileDefault.vue')
},
beforeEnter (to, from, next) {
store.dispatch('fetchPlace', to.params.id).then(() => {
if (store.state.placeModule.selectedPlace === undefined) {
next({ name: 'NotFound' })
} else {
next()
}
})
}
}
]
What I've tried:
- Changing new google.maps.places.PlacesService to new window.new google.maps.places.PlacesService
- Using beforeRouteEnter() rather than beforeEnter() as the navigation guard
- Changing google.maps... to gmapApi.google.maps... and gmapApi.maps...
- Screaming into the abyss
- Questioning every decision I've ever made
EDIT: I've also tried the this.$gmapApiPromiseLazy() proposed in the wiki here
The plugin adds a mixin providing this.$gmapApiPromiseLazy to Vue instances (components) only but you're in luck... it also adds the same method to Vue statically
Source code
Vue.mixin({
created () {
this.$gmapApiPromiseLazy = gmapApiPromiseLazy
}
})
Vue.$gmapApiPromiseLazy = gmapApiPromiseLazy
So all you need to do in your store or router is use
import Vue from 'vue'
// snip...
Vue.$gmapApiPromiseLazy().then(() => {
let service = new google.maps.places....
})
I'm trying to create a menu tree with vue-router by ajax request,but the $mount function was called before the ajax request responsed, so the router in the Vue instance always null.
Is there any good solution here?
Here is my code (index.js):
import Vue from 'vue';
import Element from 'element-ui';
import entry from './App.vue';
import VueRouter from 'vue-router';
import VueResource from 'vue-resource';
import Vuex from 'vuex'
import configRouter from './route.config';
import SideNav from './components/side-nav';
import Css from './assets/styles/common.css';
import bus from './event-bus';
import dynamicRouterConfig from './dynamic.router';
Vue.use(VueRouter);
Vue.use(Element);
Vue.use(VueResource);
Vue.use(Vuex);
Vue.http.interceptors.push((request, next) => {
bus.$emit('toggleLoading');
next(() => {
bus.$emit('toggleLoading');
})
})
Vue.component('side-nav', SideNav);
app = new Vue({
afterMounted(){
console.info(123);
},
render: h => h(entry),
router: configRouter
});
app.$mount('#app');
route.config.js:
import navConfig from './nav.config';
import dynamicRouterConfig from './dynamic.router';
let route = [{
path: '/',
redirect: '/quickstart',
component: require('./pages/component.vue'),
children: []
}];
const registerRoute = (config) => {
//require(`./docs/zh-cn${page.path}.md`)
//require(`./docs/home.md`)
function addRoute(page) {
if (page.show == false) {
return false;
}
let component = page.path === '/changelog' ? require('./pages/changelog.vue') : require(`./views/alert.vue`);
if (page.path === '/edit') {
component = require('./views/edit.vue');
}
let com = component.default || component;
let child = {
path: page.path.slice(1),
meta: {
title: page.title || page.name,
description: page.description
},
component: com
};
route[0].children.push(child);
}
//if (config && config.length>0) {
config.map(nav => {
if (nav.groups) {
nav.groups.map(group => {
group.list.map(page => {
addRoute(page);
});
});
} else if (nav.children) {
nav.children.map(page => {
addRoute(page);
});
} else {
addRoute(nav);
}
});
//}
return { route, navs: config };
};
const myroute = registerRoute(navConfig);
let guideRoute = {
path: '/guide',
name: 'Guide',
redirect: '/guide/design',
component: require('./pages/guide.vue'),
children: [{
path: 'design',
name: 'Design',
component: require('./pages/design.vue')
}, {
path: 'nav',
name: 'Navigation',
component: require('./pages/nav.vue')
}]
};
let resourceRoute = {
path: '/resource',
name: 'Resource',
component: require('./pages/resource.vue')
};
let indexRoute = {
path: '/',
name: 'Home',
component: require('./pages/index.vue')
};
let dynaRoute = registerRoute(dynamicRouterConfig).route;
myroute.route = myroute.route.concat([indexRoute, guideRoute, resourceRoute]);
myroute.route.push({
path: '*',
component: require('./docs/home.md')
});
export const navs = myroute.navs;
export default myroute.route;
And dynamic.router.js:
module.exports = [
{
"name": "Edit",
"path": "/edit"
}
]
Now the static route config is woking fine ,but how can I load data from server side by ajax request in the route.config.js instead of static data.
Waiting for some async request at page render is fine, just set empty initial values in the data section of component like:
data() {
someStr: '',
someList: []
}
and make sure you handle the empty values well without undefined errors trying to read things like someList[0].foo.
Then when the request comes back, set the initially empty values to those real data you get from the request.
Giving the user some visual indicate that you're fetching the data would be a good practice. I've found v-loading in element-ui useful for that.