ONLY ON SAFARI BROWSER.
When I manually write the url on navigation bar after logged in, ex: "http://x.x.x.x:8080/gestione", the browser loses the vuex store state (so does the gest_user module with the currentUser) and redirect to login component, I'M USING VUEX-PERSISTEDSTATE:
routes:
const routes = [
{
id: "login",
path: "/login",
name: "login",
component: Login,
meta: {
...meta.default
}
},
{
id: "home",
path: "/",
name: "homeriluser",
component: HomeUtente,
meta: {
...meta.public, authorize: [Role.user, Role.admin]
},
},
{
id: "gestione",
path: "/gestione",
name: "gestrilevazioni",
component: GestRils,
meta: {
...meta.admin, authorize: [Role.admin]
}
},
{
path: "/modifica",
name: "modificaril",
component: EditRil,
meta: {
...meta.public, authorize: [Role.user, Role.admin]
}
},
{
path: "*",
name: "page404",
component: Pagenotfound,
meta: {
...meta.public
}
}
];
my router:
import Vue from 'vue';
import VueRouter from 'vue-router';
import routes from "./router";
import store from "#/store/index"
Vue.use(VueRouter);
const router = new VueRouter({
routes,
mode: "history"
});
router.beforeEach(async (to, from, next) => {
// redirect to login page if not logged in and trying to access a restricted page
const { auth } = to.meta;
const { authorize } = to.meta;
const currentUser = store.state.gest_user;
if (auth) {
if (!currentUser.username) {
// not logged in so redirect to login page with the return url
return next({ path: '/login', query: { returnUrl: to.path } });
}
// check if route is restricted by role
if (authorize && authorize.length && !authorize.includes(currentUser.roles)) {
// role not authorised so redirect to home page
return next({ path: '/' });
}
}
next();
});
export default router;
my store:
import createPersistedState from "vuex-persistedstate";
Vue.use(Vuex);
Vue.prototype.$http = axios;
Vue.prototype.axios = axios;
const debug = process.env.NODE_ENV !== "production";
const customPersist = createPersistedState({
paths: ["gest_user", "gest_rilevazione.rilevazione"],
storage: {
getItem: key => sessionStorage.getItem(key),
setItem: (key, value) => {
sessionStorage.setItem(key, value)
},
removeItem: key => sessionStorage.removeItem(key)
}
});
const store = new Vuex.Store({
modules: {
pubblico: pubblico,
amministrazione: amministrazione,
loading_console: loading_console,
login_console: login_console,
gest_itemconc: gest_itemconc,
gest_rilslave: gest_rilslave,
gest_rilmaster: gest_rilmaster,
sidebar_console: sidebar_console,
gest_user: gest_user,
gest_newril: gest_newril,
gest_rilevazione: gest_rilevazione
},
plugins: [customPersist],
strict: debug
});
export default store;
Can anyone tell me why this happens?
You are probably using browser's local storage to persist data. Could you check if local storage is enabled in your Safari browser?
Related
I have some issues with Vue 3 and Vuex.
I'm trying to redirect users when logged in in my Vuex file, but it's not working as expected.
It's not returning any errors, it's changing a link, but not redirected to another page.
My action looks like this;
actions: {
async login(commit: any, payload: any ) {
const cookie_token = useCookies();
API.post('/login', {
email: payload.email.value,
password: payload.password.value
})
.then((response: any) => {
notif.success('Welcome back, ' + response.data.player.name)
cookie.set('user', response.data)
commit.commit('loginSuccess', response.data)
router.push({
name: 'index',
})
}).catch((e) => (
console.log(e.message)
)
)
}
},
And the router is getting files where routes are defined.
And here is my full router file:
import {
createRouter as createClientRouter,
createWebHistory,
} from 'vue-router'
import * as NProgress from 'nprogress'
// import routes from 'pages-generated'
import type { RouteRecordRaw } from "vue-router";
// Then we can define our routes
const routes: RouteRecordRaw[] = [
// This is a simple route
{
component: () => import("/#src/pages/index.vue"),
name: "index",
path: "/",
props: true,
},
{
component: () => import("/#src/pages/auth.vue"),
path: "/auth",
props: true,
children: [
{
component: () => import("/#src/pages/auth/login.vue"),
name: "auth-login",
path: "login-1",
props: true,
},
{
component: () => import("/#src/pages/auth/login.vue"),
name: "auth-signup",
path: "singup",
props: true,
},
],
},
{
component: () => import("/#src/pages/[...all].vue"),
name: "404",
path: "/:all(.*)",
props: true,
},
];
export function createRouter() {
const router = createClientRouter({
history: createWebHistory(),
routes,
})
/**
* Handle NProgress display on-page changes
*/
router.beforeEach(() => {
NProgress.start()
})
router.afterEach(() => {
NProgress.done()
})
return router
}
export default createRouter()
Have in mind that it's working on other files, and I can see that router is triggered here, but not chaning a page, only on vuex is not working. If it's not working, why there is no error?
You're creating more than one instance of Vue Router. You should export the same router instance, not a function creating a new instance each time it gets called.
The following code will likely yield the desired outcome:
import {
createRouter,
createWebHistory,
} from 'vue-router'
import * as NProgress from 'nprogress'
import type { RouteRecordRaw } from "vue-router";
const routes: RouteRecordRaw[] = [
// your routes here...
];
let router;
export function useRouter() {
if (!router) {
router = createRouter({
history: createWebHistory(),
routes,
})
router.beforeEach(() => {
NProgress.start()
})
router.afterEach(() => {
NProgress.done()
})
}
return router
}
I'm placing the router instance in the outer scope, so you always get the same instance when calling useRouter().
Consume it using:
import { useRouter } from '../path/to/router'
const router = useRouter();
I am using vue and firebase.
I want to add the redirect method using vue-router.
In my vue-router code, I have meta: { requiresAuth: true } in the multiple pages for the middleware.
My vue-router redirect method is, if jwt token is not stored in the local storage, the url redirects to /login.
I am using firebase, so I think the user account token is stored in the local storage when the user logs in.
So if my vuex code is correct, my vue-router code supposed to work properly.
Now, if I login as a user, the url doesn't change. But if I enter the
specific user's dashboard page, the redirect is working.
Why doesn't the url change when I log in?
import Vue from 'vue'
import VueRouter from 'vue-router'
//import Home from '../views/Home.vue'
import Dashboard from '../views/Dashboard.vue'
import OrdersMobile from '../views/OrdersMobile.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: () => import(/* webpackChunkName: "about" */ '../selfonboarding/Home.vue')
},
{
path: '/login',
name: 'Login',
component: () => import(/* webpackChunkName: "about" */ '../components/Login.vue')
},
{
path: '/dashboard/',
name: 'Dashboard',
component: Dashboard,
meta: { requiresAuth: true },
children: [
{
path: 'products/:id',
name: 'Products',
component: () => import(/* webpackChunkName: "about" */ '../views/Products.vue')
},
{
path: 'working-hours/:id',
name: 'WorkingHours',
component: () => import(/* webpackChunkName: "about" */ '../views/WorkingHours.vue')
},
// {
// path: 'pictures/:id',
// name: 'Pictures',
// component: Pictures,
// },
{
path: 'orders/:id',
name: 'Orders',
component: () => import(/* webpackChunkName: "about" */ '../views/Orders.vue')
},
{
path: 'orders.s/:id',
name: 'OrdersMobile',
component: OrdersMobile,
children: [
{
path: 'processed',
name: 'Processed',
component: () => import(/* webpackChunkName: "about" */ '../views/Processed.vue')
}
]
},
{
path: 'information/:id',
name: 'Information',
component: () => import(/* webpackChunkName: "about" */ '../views/Information.vue')
},
{
path: 'information.s/:id',
name: 'InformationMobile',
component: () => import(/* webpackChunkName: "about" */ '../views/InformationMobile.vue')
},
]
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
})
router.beforeEach((to, from, next) => {
if(to.matched.some(record => record.meta.requiresAuth)) {
if (localStorage.getItem('jwt') == null) {
next({
path: '/login',
params: { nextUrl: to.fullPath }
})
}
} else {
next()
}
})
export default router
vuex code
../store/user.js
import 'firebase/firebase-auth'
import fireApp from '#/plugins/firebase'
import router from '../../router'
const firebase = require("firebase");
require("firebase/firestore");
const db = firebase.firestore();
const state = {
currentUser: null
}
const getters = {
currentUser: state => state.currentUser
}
const mutations = {
userStatus: (state, user) => {
user === null ? state.currentUser = null : state.currentUser = user.email
}
}
const actions = {
signIn: async ({ commit }, user) => {
try {
const userData = await fireApp.auth().signInWithEmailAndPassword(
user.email,
user.password
);
// Get the user id (from the user object I guess)
const userId = fireApp.auth().currentUser.uid;
// or maybe through const userId = fireApp.auth().currentUser.uid;
const proUserDocRef = db.collection('ProUser').doc(userId);
proUserDocRef.get().then((doc) => {
if(doc.exists && doc.data().status === true) {
router.push({name:'Products',params:{id: userId}}).catch(err => {})
} else if(doc.exists && doc.data().status === false){
router.push({name:'Welcome',params:{id: userId}}).catch(err => {})
} else {
alert('You are not registered as a pro user.')
}
})
}
catch(error) {
const errorCode = error.code
const errorMesage = error.message
if(errorCode === 'auth/wrong-password') {
alert('wrong password')
} else {
alert(errorMesage)
}
}
},
signOut: async({ commit }) => {
try {
await fireApp.auth().signOut()
}
catch(error) {
alert(`error sign out, ${error}`)
}
commit('userStatus', null)
}
}
export default {
state,
mutations,
getters,
actions
}
The beforeEach navigation guard is missing a next() call when the route requires authentication and you are logged in:
router.beforeEach((to, from, next) => {
if(to.matched.some(record => record.meta.requiresAuth)) {
if (localStorage.getItem('jwt') == null) {
next({
path: '/login',
params: { nextUrl: to.fullPath }
})
} else {
next(); // Add this ✅
}
} else {
next()
}
})
I added
const token = await firebase.auth().currentUser.getIdToken(true)
localStorage.setItem('jwt', token)
in user.js actions section.
Then, I could make it.
I couldn't set the jwt token in the local storage.
So I did when I log in to the website.
Also I was missing to add next().
I have a problem, which seemed to be simple but now is not so simple (for me):
I have set up a Vue project with the vue-cli (Router, VueX, PWA). I have set up some routes as always (following straight the recommendations of the documentation) and some state-fields in VueX:
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '#/views/Home.vue'
import Login from '#/views/Login.vue'
import Logout from '#/components/functional/Logout.vue'
import Datapoints from '#/views/Datapoints.vue'
import store from '../store'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: Home,
meta: {
requiresAuth: true
}
},
{
path: '/login',
name: 'login',
component: Login,
meta: {
requiresGuest: true
}
},
{
path: '/logout',
name: 'logout',
component: Logout
},
{
path: '/project/:id',
name: 'project',
component: Datapoints
}
]
const router = new VueRouter({
mode: 'history',
routes
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.isAuthenticated) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next()
}
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresGuest)) {
if (store.getters.isAuthenticated) {
next({
path: '/',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next()
}
})
export default router
In my Views / Components, i use the push method on the $router instance, to rote programatically, like this:
this.$router.push({ name: 'home', params: { status: project.response.status, message: project.response.data.error }})
, where project is the result of an awaited axios HTTP request.
My Problem
Every time i push programatically a new route, or use the router-view element, my page reloads (what i want to prevent in a SPA / PWA ...)
My Vue instance:
new Vue({
router,
store,
vuetify,
render: h => h(App)
}).$mount('#app');
I would be happy if anynoe could help me out to not reload the page on every route change with the Vue router.
I think the reload behavior is related to the history mode of your router
const router = new VueRouter({
mode: 'history',
routes
})
You can try remove the history mode and see what happens. If you want to keep it, please check with the history mode doc to see if all settings have been done correctly.
https://router.vuejs.org/guide/essentials/history-mode.html.
Hope this helps.
you can try using vuex-persistedstate to maintain data in store an it won't change whenever the page is refreshed.
`import createPersistedState from "vuex-persistedstate
const store = new Vuex.Store({
// ...
plugins: [createPersistedState()]
});
Visit https://www.npmjs.com/package/vuex-persistedstate
I'm building an app with Laravel + VueJS. In order to restrict some routes, I use navigation guards. The problem is that I need to access Vuex mutators in order to know if the current user is logged in. The thing is that store is defined, but I cannot use the mutator from the router. I got this error: TypeError: Cannot read property 'commit' of undefined but as I said, store is well defined. Does someone have an idea ? Thanks !
routes
import Vue from 'vue'
import Router from 'vue-router'
import Hello from '#/components/Hello'
import Register from '#/components/Register'
import Login from '#/components/Login'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Hello',
component: Hello,
meta: {
showNavigation: true,
requireAuthentication: true
}
},
{
path: '/register',
component: Register,
meta: {
showNavigation: false,
requireAuthentication: false
}
},
{
path: '/login',
component: Login,
meta: {
showNavigation: false,
requireAuthentication: false
}
}
],
mode: 'history'
})
store
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
access_token: null,
expires_in: 0
},
mutations: {
setToken (state, response) {
state.access_token = response.body.access_token
state.expires_in = response.body.expires_in + Date.now()
},
getToken (state) {
if (!state.access_token || !state.expires_in) return null
if (state.expires_in < Date.now()) this.commit('destroyToken')
return state.access_token
},
destroyToken (state) {
state.access_token = null
state.expires_in = 0
},
isAuthenticated (state) {
return this.commit('getToken') !== null
}
},
actions: {
getOauthToken (context, user) {
var data = {
client_id: 2,
client_secret: 'XXXXXXXXXXXXXXXXXXXXXXXXXX',
grant_type: 'password',
username: user.email,
password: user.password
}
Vue.http.post('oauth/token', data)
.then(response => {
context.commit('setToken', response)
})
}
}
})
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import { store } from './store'
import VueResource from 'vue-resource'
import VeeValidate from 'vee-validate'
Vue.config.productionTip = false
Vue.use(VueResource)
Vue.use(VeeValidate)
Vue.http.options.root = 'http://codex.app'
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requireAuthentication)) {
console.log(store)
console.log(store.commit('isAuthenticated'))
if (!store.commit('isAuthenticated')) {
next('/login')
} else {
next()
}
} else {
next()
}
})
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
when we commit mutations, it refers to change the state and ONLY the state.when you need more complex mutations to change the state, using actions instead.
My index route is defined as:
{ path: '/', adminOnly: false, component: Main },
Is there a way to access the 'adminOnly' property?
There seems to be no way of doing so in this block of code:
routes.beforeEach((to, from, next) => {
console.log(to)
next();
});
My routes file:
import Vue from 'vue';
import VueRouter from 'vue-router';
import Main from '../components/Main.vue';
import About from '../components/About.vue';
const NotFoundException = {
template: '<div>Route Was Not Found</div>'
};
Vue.use(VueRouter);
const routes = new VueRouter({
mode: 'history',
hashbang: false,
linkActiveClass: 'active',
base: '/jokes',
routes: [
{ path: '/', adminOnly: false, component: Main },
{ path: '/about', adminOnly: false, component: About },
{ path: '*', adminOnly: false, component: NotFoundException }
]
});
routes.mode = 'html5';
routes.beforeEach((to, from, next) => {
// CHECK IF ADMINONLY EXISTS
next();
});
export default routes;
I did get a solution by adding a mixin of adminOnly.js which has the following code in it:
But then again, the mixin has to be added to each component if I was to redirect the user to the login page if not admin.
//Just a test
var isAdmin = false;
export default {
beforeRouteEnter (to, from, next) {
if(!isAdmin) {
next((vm) => {
vm.$router.push('/login');
});
}
}
}
Yes there is better way to handle this. Every route object can have meta field. Just wrap adminOnly property in meta object:
routes: [
{ path: '/', meta: { adminOnly: false }, component: Main },
{ path: '/about', meta: { adminOnly: false }, component: About },
{ path: '*', meta: { adminOnly: false }, component: NotFoundException }
]
Check route for admin rights:
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.adminOnly)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
}
}
For more please check docs and I created little example on jsFiddle