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();
Related
When reloading page, I want to get current route. Sadly route.name is undefined, router.currentRoute is RefImpl object, which has correct route with '/team' inside. router.currentRoute.value is just root '/', not '/team', as expected. Is it possible to get correct value from RefImpl?
import { useRouter, useRoute } from 'vue-router'
export default {
name: 'Canvas',
components: { Ring2, Map },
setup() {
const router = useRouter()
const route = useRoute()
onMounted(() => {
console.log(route.name) //undefined
console.log(router.currentRoute) //RefImpl with correct value ('/team')
console.log(router.currentRoute.value) // route is '/'
const rawObject = {...router.currentRoute}
console.log(rawObject) // value is '/'
...
Router is set up like this:
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/team',
name: 'Team',
component: () => import('../views/Team.vue')
},
...
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
This happens because the router hasn't yet completely resolved the initial navigation when you refresh the page, so route refers to the default (/) initially in onMounted.
Vue Router's isReady() returns a Promise that resolves when the router has completed the initial navigation, so your onMounted hook can await that Promise before trying to read route.path:
import { useRoute, useRouter } from 'vue-router'
import { onMounted } from 'vue'
export default {
setup() {
const route = useRoute()
const router = useRouter()
onMounted(async () => {
await router.isReady() 👈
console.log('route.path', route.path)
})
},
}
demo
Try using the composable function useRoute :
import {
useRoute
} from 'vue-router'
import {
computed
} from 'vue'
setup() {
const route = useRoute();
const path = computed(() => route.path)
}
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?
I'am trying to use vue-router in my store.js file methods to redirect a user after performing a log in but for whatever reason it just send him back to the previous page. If i use vue-router in any of my .vue pages it works just fine.
What am i doing wrong?
here is my routes.js file
import Vue from 'vue'
import VueRouter from 'vue-router'
import login from '#/views/login'
import page2 from '#/views/page2 '
Vue.use(VueRouter)
export default new VueRouter({
routes: [
{
path: '/',
name: 'login',
component: login
},
{
path: '/page2 ',
name: 'page2 ',
component: page2
}
]
})
Here is my store.js file
import Vue from 'vue'
import Vuex from 'vuex'
import axios from '#/axios.js'
import router from '#/routes';
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
token: ''
},
mutations: {
login (state, payload){
return state.token = payload;
}
},
actions: {
login ({ commit }, payload) {
axios.get('/login',{
auth: {
username: payload.username,
password: payload.password
}
}).then(function (response) {
commit('login', response.data['user-token'])
router.go('page2')
})
}
}
})
export default store;
router.go(n) is used to navigate in the history stack: https://router.vuejs.org/guide/essentials/navigation.html#router-go-n
Use router.push({name: "page2"}) instead.
I have an Angular application using an Express.js backend. It features a default address pathway to a login page /avior/login. I need to write an E2E Test for the whole application and I just started writing the first test part which is supposed to recognize the login page and login itself and then check whether the redirect was successful. I have wrote this error-prone code so far:
import { AppPage } from './app.po';
import { browser, logging, element } from 'protractor';
import { async, ComponentFixture, fakeAsync, TestBed, tick,
} from '#angular/core/testing';
import {AppComponent} from '../../src/app/app.component'
let comp: AppComponent;
let fixture: ComponentFixture<AppComponent>;
let page: AppPage;
let router: Router;
let location: SpyLocation;
let username: 'Test';
let password: 'testtest';
let usernameInput: element(by.css('#inputUser'));
let passwordInput: element(by.css('#inputPassword'));
let loginButton: element(by.css('#loginSubmit'));
describe('Avior App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should navigate to "/avior/login" immediately', fakeAsync(() => {
createComponent();
tick(); // wait for async data to arrive
expect(location.path()).toEqual('/avior/login', 'after initialNavigation()');
expectElementOf(DashboardComponent);
}));
it('should display Login message in the header', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Anmeldung');
});
it('there should be username and password login fields', () => {
// how to check?
});
it('login using false credentials should make you stay put and throw an error', () => {
// how to?
});
it('login using Test:testtset (=successful credentials => successful login) should redirect to /avior/dashboard', () => {
// how to?
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});
function createComponent() {
fixture = TestBed.createComponent(AppComponent);
comp = fixture.componentInstance;
const injector = fixture.debugElement.injector;
location = injector.get(Location) as SpyLocation;
router = injector.get(Router);
router.initialNavigation();
spyOn(injector.get(TwainService), 'getQuote')
// fake fast async observable
.and.returnValue(asyncData('Test Quote'));
advance();
page = new Page();
}
function loginFn() {
usernameInput.sendKeys(username);
passwordInput.sendKeys(password);
loginButton.click();
};
How to fix this code?
UPDATE
I tried simplifying my code to only include routing tests so far and I get new errors thrown out.
My new code is the following:
import { AppPage } from './app.po';
import { browser, logging, element } from 'protractor';
import { async, ComponentFixture, fakeAsync, TestBed, tick,
} from '#angular/core/testing';
import {AppComponent} from '../../src/app/app.component'
import {Component} from "#angular/core";
import {RouterTestingModule} from "#angular/router/testing";
import {Routes} from "#angular/router";
import {Router} from "#angular/router";
import { AviorComponent } from '../../src/app/avior/avior.component';
import { DashboardComponent } from '../../src/app/avior/dashboard/dashboard.component';
import { ProfileComponent } from '../../src/app/avior/profile/profile.component';
import { SettingsComponent } from '../../src/app/avior/settings/settings.component';
import { LoginComponent } from '../../src/app/avior/login/login.component';
import { PageNotFoundComponent } from '../../src/app/avior/page-not-found/page-not-found.component';
import { InfoComponent } from '../../src/app/avior/info/info.component';
import { UsersComponent } from '../../src/app/avior/users/users.component';
import { MandatorsComponent } from '../../src/app/avior/mandators/mandators.component';
import { SystemComponent } from '../../src/app/avior/system/system.component';
import { WorkflowsComponent } from '../../src/app/avior/workflows/workflows.component';
import { MessagesComponent } from '../../src/app/avior/messages/messages.component';
import { BrowserDynamicTestingModule,
platformBrowserDynamicTesting } from '#angular/platform-browser-dynamic/testing';
/* let comp: AppComponent;
let fixture: ComponentFixture<AppComponent>;
let page: AppPage;
let router: Router;
let location: SpyLocation;
let username: 'Chad';
let password: 'chadchad';
let usernameInput: element(by.css('#inputUser'));
let passwordInput: element(by.css('#inputPassword'));
let loginButton: element(by.css('#loginSubmit')); */
const aviorRoutes: Routes = [{
path: '', component: AviorComponent,
children: [
{
path: '', component: ProfileComponent
},
{
path: 'dashboard', component: DashboardComponent,
data: {title: 'Dashboard'},
},
{
path: 'login', component: LoginComponent,
data: {title: 'Login'},
},
{
path: 'logout', component: LoginComponent,
data: {title: 'Login'},
},
{
path: 'profile', component: ProfileComponent,
data: {title: 'Profil'},
},
{
path: 'settings', component: SettingsComponent,
data: {title: 'Einstellungen'},
},
{
path: 'info', component: InfoComponent,
data: {title: 'Info'},
},
{
path: 'users', component: UsersComponent,
data: {title: 'Benutzer'},
},
{
path: 'workflows', component: WorkflowsComponent,
data: {title: 'Workflows'},
},
{
path: 'system', component: SystemComponent,
data: {title: 'System'},
},
{
path: 'mandators', component: MandatorsComponent,
data: {title: 'Mandanten'}
},
{
path: 'messages', component: MessagesComponent,
data: {title: 'Nachrichten'}
},
{
path: '404', component: PageNotFoundComponent,
data: {title: 'Page not found'},
}
]
}];
describe('Avior App', () => {
let page: AppPage;
beforeEach(() => {
TestBed.resetTestEnvironment();
TestBed.initTestEnvironment(BrowserDynamicTestingModule,
platformBrowserDynamicTesting())
TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes(aviorRoutes)],
declarations: [
DashboardComponent,
ProfileComponent,
SettingsComponent,
LoginComponent,
PageNotFoundComponent,
InfoComponent,
UsersComponent,
MandatorsComponent,
SystemComponent,
WorkflowsComponent,
MessagesComponent
]
});
page = new AppPage();
this.router = TestBed.get(Router);
location = TestBed.get(Location);
this.fixture = TestBed.createComponent(AppComponent);
this.router.initialNavigation();
});
/* it('should navigate to "/avior/login" immediately', fakeAsync(() => {
createComponent();
tick(); // wait for async data to arrive
expect(location.path()).toEqual('/avior/login', 'after initialNavigation()');
expectElementOf(DashboardComponent);
})); */
it('navigate to "" redirects you to /avior/login', fakeAsync(() => {
 this.router.navigate(['']);
tick();
expect(location.path()).toBe('/avior/login');
}));
it('navigate to "dashboard" takes you to /dashboard', fakeAsync(() => {
 this.router.navigate(['dashboard']);
 tick();
expect(location.path()).toBe('avior/dashboard');
}));
it('should display Anmeldung message in the header', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Anmeldung');
});
it('there should be username and password login fields', () => {
});
it('login using false credentials should make you stay put and throw an error', () => {
});
it('login using Chad:chadchad should redirect to /avior/dashboard', () => {
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});
/* function createComponent() {
fixture = TestBed.createComponent(AppComponent);
comp = fixture.componentInstance;
const injector = fixture.debugElement.injector;
location = injector.get(Location) as SpyLocation;
router = injector.get(Router);
router.initialNavigation();
spyOn(injector.get(TwainService), 'getQuote')
// fake fast async observable
.and.returnValue(asyncData('Test Quote'));
advance();
page = new Page();
}
function loginFn(username, password) {
usernameInput.sendKeys(username);
passwordInput.sendKeys(password);
loginButton.click();
};
*/
The errors get thrown out during ng e2e runtime:
- Failed: Can't resolve all parameters for ApplicationModule: (?). - Failed: Cannot read property 'assertPresent' of null
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.