hello guys i follow everything provide no google but none of these working i m new to vuex 3.0.1 and vue 2.5 below of my code
import Vue from 'vue'
import App from './App'
import router from './router'
import axios from 'axios'
import store from './store'
Vue.prototype.$http = axios;
var VueResource = require('vue-resource');
Vue.use(VueResource);
router.beforeEach((to, from, next) => {
console.log(this.$store.state.authUser)// this is not working
if (to.matched.some(record => record.meta.requiresAuth) &&
(!this.$store.state.authUser ||
this.$store.state.authUser === 'null')){
const authUser = localStorage.getItem('authUser')
console.log('here log');
next({ path: '/', })
}else{
console.log('bhar else redirect');
next()
}
});
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: {
App
}
})
everything work porperly if i remove this this.$store.state.authUser it work here is my store.js file
import Vue from 'vue'
import Vuex from 'vuex'
import userStore from './user/userStore.js'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
modules:{
userStore
},
strict:debug
})
here is my userStore
const state = {
authUser:null
}
const mutations = {
SET_AUTH_USER(state,userObj){
state.authUser = userObj
}
}
const actions = {
setUserObject :({commit},userObj) =>{
commit('SET_AUTH_USER',userObj)
}
}
export default {
state, mutations, actions
}
here is my login succcess from where i trie to dispatch value in store note i have value in store
this.$store.dispatch('setUserObject',response.data.id)
i does everything properly but don't know why it throw error Cannot read property 'state' of undefined
You are accessing your store in the router's beforeEach guard using this.$store. But this is not your vue instance and has no $store property.
Since you are importing the store using import store from './store', you can use that store import to access your store as follows:
router.beforeEach((to, from, next) => {
console.log(store.state.userStore.authUser)// this will log put authUser from userStore module
if (to.matched.some(record => record.meta.requiresAuth) &&
(!store.state.userStore.authUser ||
store.state.userStore.authUser === 'null')){
const authUser = localStorage.getItem('authUser')
console.log('here log');
next({ path: '/', })
}else{
console.log('bhar else redirect');
next()
}
});
The authUser property belongs to the userStore module, so to access it you use
store.state.userStore.authUser
Related
I'm using Vuejs 3, vuerouter 4 and pinia and trying to put a navigation guard in some routes, as per the example in the documentation (if a user is not authenticated and is not on the login page, send the user to login page to get authenticated). This is explained also in pinia documentation on use of pinia outside of components. But I can't get my head around it.
The store I use is currently simple (return false or true on isAuthenticated):
//authStore.js
import { defineStore } from "pinia";
export const useAuthStore = defineStore( 'AuthStore', {
state: () => {
return {
isAuthenticated: false
}
}
})
I want to use this isAuthenticated in a beforeEnter in routes/index.js
In main.js:
import { useAuthStore } from '#/stores/authStore'
import { createApp } from 'vue'
import App from './App.vue'
import router from '#/router'
import { createPinia } from 'pinia'
const app = createApp(App)
app.use(createPinia()).use(router)
app.mount('#app')
// I'm not using authStore in this file, so this raises an error:
const authStore = useAuthStore()
And in router/index.js:
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
// path for login page,
{
path: '/adm/home',
name: 'AdmView',
component: () => import('#/views/adm/AdmView.vue'),
beforeEnter: (to) => {
// error: authStore is not defined
const isAuthenticated = authStore
if ( !isAuthenticated && to.name !== 'login-adm' ) {
return { name: 'login-adm' }
}
}
},
// other paths
]
Your authStore.js exports useAuthStore as expected, but you do not call it as required.
authStore is not defined because authStore is the filename of your auth store -- instead you should be executing the function useAuthStore exported from that file:
const authStore = useAuthStore();
console.log('Is authenticated?', authStore.isAuthenticated);
I don't know if it is the main issue but u forgot to use a destructor on userStore doing
const isAuthenticated = authStore
It supposed to be
const { isAuthenticated } = toRefs(authStore);
toRefs to preserve reactivity after passing. It can be imported as
import { toRefs } from 'vue';
I am using Vue 3 including the Composition API and additionally Pinia as State Management.
In the options API there is a method beforeRouteEnter, which is built into the component itself. Unfortunately this method does not exist in the composition API. Here the code, which would have been in the beforeRouteEnter method, is written directly into the setup method. However, this means that the component is loaded and displayed first, then the code is executed and, if the check fails, the component is redirected to an error page, for example.
My idea was to make my check directly in the route configuration in the beforeEnter method of a route. However, I don't have access to the Pinia Store, which doesn't seem to be initialized yet, although it is called before in the main.js.
Console Log
Uncaught Error: [🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?
const pinia = createPinia()
app.use(pinia)
This will fail in production.
Router.js
import { useProcessStore } from "#/store/process";
const routes: Array<RouteRecordRaw> = [
{
path: "/processes/:id",
name: "ProcessView",
component: loadView("ProcessView", "processes/"),
beforeEnter: () => {
const processStore = useProcessStore();
console.log(processStore);
},
children: [
{
path: "steer",
name: "ProcessSteer",
component: loadView("ProcessSteer", "processes/")
},
{
path: "approve/:code",
name: "ProcessApprove",
component: loadView("ProcessApprove", "processes/")
}
]
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
});
export default router;
main.js
import { createApp } from "vue";
import "#/assets/bundle-bootstrap.css";
import App from "#/App.vue";
import { createPinia } from "pinia";
import router from "#/router";
import SvgIcon from "#/components/SvgIcon.vue";
const pinia = createPinia();
const app = createApp(App);
app.use(pinia);
app.use(router);
app.component("SvgIcon", SvgIcon);
router.isReady().then(() => {
app.mount("#app");
});
However, I don't have access to the Pinia Store, which doesn't seem to be initialized yet, although it is called before in the main.js
Before what? Pinia instance is created with const pinia = createPinia(); after the router module is imported - while it is imported, all side-effects including the call to createRouter() are executed. Once the router is created it begins it's initial navigation (on client - on server you need to trigger it with router.push()) - if you happen to be at URL matching the route with guard that is using Pinia store, the useProcessStore() happens before Pinia is created...
Using a store outside of a component
You have two options:
either you make sure that any useXXXStore() call happens after Pinia is created (createPinia()) and installed (app.use(pinia))
or you pass the Pinia instance into any useXXXStore() outside of component...
// store.js
import { createPinia } from "pinia";
const pinia = createPinia();
export default pinia;
// router.js
import pinia from "#/store.js";
import { useProcessStore } from "#/store/process";
const routes: Array<RouteRecordRaw> = [
{
path: "/processes/:id",
name: "ProcessView",
component: loadView("ProcessView", "processes/"),
beforeEnter: () => {
const processStore = useProcessStore(pinia ); // <-- passing Pinia instance directly
console.log(processStore);
},
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
});
export default router;
// main.js
import { createApp } from "vue";
import App from "#/App.vue";
import store from "#/store.js";
import router from "#/router";
const app = createApp(App);
app.use(store);
app.use(router);
router.isReady().then(() => {
app.mount("#app");
});
Hope this would be helpful.
Vue provide support for some functions in which we need store(outside of the components).
To fix this problem I just called the useStore() function inside the function provided by Vue(beforeEach) and it worked.
Reference : https://pinia.vuejs.org/core-concepts/outside-component-usage.html
Example :
import { useAuthStore } from "#/stores/auth";
.
.
.
.
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
router.beforeEach(async (to, from) => {
const authStore = useAuthStore();
// use authStore Here
});
I have same problem to access the store in "beforeEach" method for managing authorization.
I use this method in main.js, not in router.js. in router.js store is not accessible.
create pinia instance in piniCreate.js
//piniaCreate.js
import { createPinia } from "pinia";
const pinia = createPinia();
export default pinia;
after that create my store in mainStore.js
import { defineStore } from 'pinia'
export const mainStore = defineStore('counter', {
state: () => {
return {
user: {
isAuthenticated: isAuthen,
}
}
},
actions: {
login(result) {
//...
this.user.isAuthenticated = true;
} ,
logOff() {
this.user.isAuthenticated = false;
}
}
});
Then I used beforeEach method in the main.js
//main.js
import { createApp } from 'vue'
import App from './App.vue'
import pinia from "#/stores/piniaCreate";
import { mainStore } from '#/stores/mainStore';
import router from './router'
const app = createApp(App)
.use(pinia)
.use(router)
const store1 = mainStore();
router.beforeEach((from) => {
if (from.meta.requiresAuth && !store1.user.isAuthenticated) {
router.push({ name: 'login', query: { redirect: from.path } });
}
})
app.mount('#app');
You can pass the method in the second parameter of definestore:
store.js
export const useAppStore = defineStore('app', () => {
const state = reactive({
appName: 'App',
appLogo: ''
})
return {
...toRefs(state)
}
})
router.js
router.beforeEach((to, from, next) => {
const apppStore = useAppStore()
next()
})
I have resolved this by adding lazy loading
const routes = [
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
I want to apply mutations through actions to a variable in my vuejs application. But I get this error saying [vuex] unknown local mutation type: updateValue, global type: app/updateValue
Here is my store folder structure:
-store
-modules
-app
-actions.js
-getters.js
-mutations.js
-state.js
-index.js
-actions.js
-getters.js
-mutations.js
-state.js
-index.js
This is my ./store/index.js file:
import Vue from 'vue'
import Vuex from 'vuex'
import actions from './actions'
import getters from './getters'
import modules from './modules'
import mutations from './mutations'
import state from './state'
Vue.use(Vuex)
const store = new Vuex.Store({
namespaced: true,
actions,
getters,
modules,
mutations,
state
})
export default store
This is my ./store/modules/index.js:
const requireModule = require.context('.', true, /\.js$/)
const modules = {}
requireModule.keys().forEach(fileName => {
if (fileName === './index.js') return
// Replace ./ and .js
const path = fileName.replace(/(\.\/|\.js)/g, '')
const [moduleName, imported] = path.split('/')
if (!modules[moduleName]) {
modules[moduleName] = {
namespaced: true
}
}
modules[moduleName][imported] = requireModule(fileName).default
})
export default modules
This is my ./store/modules/app/actions.js:
export const updateValue = ({commit}, payload) => {
commit('updateValue', payload)
}
This is my ./store/modules/app/getters.js:
export const value = state => {
return state.wruValue;
}
This is my ./store/modules/app/mutations.js:
import { set, toggle } from '#/utils/vuex'
export default {
setDrawer: set('drawer'),
setImage: set('image'),
setColor: set('color'),
toggleDrawer: toggle('drawer')
}
export const updateValue = (state, payload) => {
state.wruValue = payload * 12;
}
This is my ./store/modules/app/state.js:
export default {
drawer: null,
color: 'green',
wruValues:1,
wruValue: 1,
}
and finally this is my vue component:
<v-btn #click="updateValue(10)">
SHOW
</v-btn>
import { mapActions } from 'vuex';
...mapActions ('app',[
'updateValue'
]),
So when I click on the button I expect to see the wruValue to change (I print the value somewhere else for testing purposes) but instead I get the error mentioned above. What's wrong with my code?
commit('updateValue', payload, {root: true})
But I find your use of namespacing odd. For my projects, I don't separate out files for getters, actions, etc, I separate out tasks, projects, companies, etc. But if it works for you, that's fine. It doesn't seem like the issue. If you still get an error, you might need to change "updateValue" to "mutations/updateValue" or something.
You should use this project structure:
src/store/modules/app.js
export const state = {
drawer: null,
color: 'green',
wruValues: 1,
wruValue: 1
}
export const mutations = {
UPDATE_VALUE: (state, payload) => {
state.wruValue = payload * 12
}
}
export const actions = {
updateValue: ({ commit }, payload) => {
commit('UPDATE_VALUE', payload)
}
}
export const getters = {
getWruValue: (state) => state.wruValue
}
src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const requireContext = require.context('./modules', true, /.*\.js$/)
const modules = requireContext.keys()
.map(file =>
[file.replace(/(^.\/)|(\.js$)/g, ''), requireContext(file)]
)
.reduce((modules, [name, module]) => {
if (module.namespaced === undefined) {
module.namespaced = true
}
return { ...modules, [name]: module }
}, {})
export default new Vuex.Store({
modules
})
src/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store/'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
src/App.vue
<template>
<div id="app">
<button #click="updateValue(10)">
SHOW
</button>
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default {
methods: {
...mapActions('app', ['updateValue'])
}
}
</script>
Then if you want to add a new store namespace, u need to put it inside of src/modules folder.
I am trying to verify if the user is authenticated to be able to give access to the route that is directed otherwise redirect to the login route, the problem is that I do not know how to execute the fetchUser action from my beforeEach. In other words, I can't access my getter from the router script.
store.js
import mutations from './mutations';
import actions from './actions';
import getters from './getters';
export default {
state: {
isLoggedIn: !!localStorage.getItem("token"),
user_data : localStorage.getItem("user_data"),
},
getters ,
mutations,
actions
}
routes/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
import routes from './rutas';
import store from '../store/';
const router = new VueRouter({
mode : 'history',
routes
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.isLoggedIn) {
next({path: '/login'})
}
else {
store.dispatch('fetchUser') // Line error
next()
}
}
else {
next() // make sure to always call next()!
}
})
getters.js
export default {
isLoggedIn: state => {
return state.isLoggedIn
},
user_name : state =>{
if(! _.isEmpty(this.user_data))
return JSON.parse(state.user_data).name
return '';
},
isEmptyUser : state =>{
return _.isEmpty(this.user_data);
},
isAdmin: state => {
if(! _.isEmpty(this.user_data)) return state.user_data.nivel===1
return false;
}
}
actions.js
export default {
/* more methods*/
, async fetchUser({ commit }) {
return await axios.post('/api/auth/me')
.then(res => {
setTimeout(() => {
localStorage.setItem("user_data", JSON.stringify(res.data));
Promise.resolve(res.data);
}, 1000);
},
error => {
Promise.reject(error);
});
}
This returns error in console:
_WEBPACK_IMPORTED_MODULE_3__store___.a.dispatch is not a function
How can I do or the approach is the wrong one and I should not access actions directly?
The problem is your store is not the actual store object, it is just the object used to generate it.
A solution is to have the file export the real store:
import Vue from 'vue';
import Vuex from 'vuex';
import mutations from './mutations';
import actions from './actions';
import getters from './getters';
Vue.use(Vuex); // added here
export default new Vuex.Store({ // changed here
state: {
isLoggedIn: !!localStorage.getItem("token"),
user_data : localStorage.getItem("user_data"),
},
getters ,
mutations,
actions
}) // changed here
Now your router code would work.
What you must be aware as well is that somewhere, probably in your main.js, you had the store being initialized like above. For example:
import store from '../store/';
new Vue({
store: new Vuex.Store(store),
// ...
})
Now you must remove that initialization and use the store directly:
import store from '../store/';
new Vue({
store: store, // or simply store
// ...
})
And all should be good.
I’m trying to use lazy loading with my vuex modules like this article : https://alexjoverm.github.io/2017/07/16/Lazy-load-in-Vue-using-Webpack-s-code-splitting/
Here is my old store\index.js :
import Vue from 'vue';
import Vuex from 'vuex';
import app from './modules/app';
import search from './modules/search';
import identity from './modules/identity';
import profil from './modules/profil';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
app,
search,
identity,
profil,
},
});
I tried to do this :
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store();
import('./modules/app').then((appModule) => {
store.registerModule('app', appModule);
});
import('./modules/search').then((searchModule) => {
store.registerModule('search', searchModule);
});
import('./modules/identity').then((identityModule) => {
store.registerModule('identity', identityModule);
});
import('./modules/profil').then((profilModule) => {
store.registerModule('profil', profilModule);
});
export default store;
But now I have a lot of error like “TypeError: _vm.consultList is undefined", consultList is a mapState variable, I also have same errors on my mapActions
Did I’ve done something wrong ?
All of those modules will be registered when app is loaded any because you most likely add the store to your initial vue instance. How I dynamically loading my vuex module is via the router:
{
path: "/orders/active",
name: "active-orders",
component: ActiveOrders,
props: true,
beforeEnter: (to, from, next) => {
importOrdersState().then(() => {
next();
});
}
},
Then also inside my router file I added:
const importOrdersState = () =>
import("#/store/orders").then(({ orders }) => {
if (!store.state.orders) store.registerModule("orders", orders);
else return;
});