Vuex Modules issue with _mapGetters() : getters should be function - vue.js

I am trying to restructure my project using Vuex Modules.
If everything was running fine previously, I am now getting an error in my App.vue component, related to __mapGetters w module
vuex.esm.js?358c:97 Uncaught Error: [vuex] getters should be function but "getters.isAuthenticated" in module "login" is false.
The nav links are using : v-if="isAuthenticated" which is a getter in the Login module
#/App.vue
<template>
<div id="app">
<header id="header">
<nav>
<ul class="navigation">
<li id="home"><router-link :to="{ name: 'home' }">Home</router-link></li>
<li id="login" v-if="!isAuthenticated"><router-link :to="{ name: 'login' }">Login</router-link></li>
....
</template>
<script>
import store from '#/vuex/store'
import router from '#/router/index'
import { mapGetters } from 'vuex'
export default {
name: 'app',
computed: {
...mapGetters({ isAuthenticated: 'isAuthenticated' })
},
methods: {
logout () {
return this.$store.dispatch('logout')
.then(() => {
window.localStorage.removeItem('vue-authenticate.vueauth_token')
this.$router.push({ name: 'home' })
})
}
},
store,
router
}
</script>
my vuex project structure is now :
src
|_ vuex
L_ modules
L_ login
| |_ index.js
| |_ mutation_types.js
|_ shoppinglist
L_ index.js
|_ mutation_types.js
|_ App.vue
|_ main.js
#/vuex/store
import Vue from 'vue'
import Vuex from 'vuex'
import login from '#/vuex/modules/login'
import shoppinglist from '#/vuex/modules/shoppinglist'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
login,
shoppinglist
}
})
#vuex/modules/login/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import * as types from './mutation_types'
import vueAuthInstance from '#/services/auth.js'
Vue.use(Vuex)
const state = {
isAuthenticated: vueAuthInstance.isAuthenticated(),
currentUserId: ''
}
const actions = {
login: ({ commit }, payload) => {
payload = payload || {}
return vueAuthInstance.login(payload.user, payload.requestOptions)
.then((response) => {
// check response user or empty
if (JSON.stringify(response.data) !== '{}') {
commit(types.IS_AUTHENTICATED, { isAuthenticated: true })
commit(types.CURRENT_USER_ID, { currentUserId: response.data.id })
return true
} else {
commit(types.IS_AUTHENTICATED, { isAuthenticated: false })
commit(types.CURRENT_USER_ID, { currentUserId: '' })
return false
}
})
},
logout: ({commit}) => {
commit(types.IS_AUTHENTICATED, { isAuthenticated: false })
commit(types.CURRENT_USER_ID, { currentUserId: '' })
return true
}}
const getters = {
isAuthenticated: (state) => {
return state.isAuthenticated
}
}
const mutations = {
[types.IS_AUTHENTICATED] (state, payload) {
state.isAuthenticated = payload.isAuthenticated
},
[types.CURRENT_USER_ID] (state, payload) {
state.currentUserId = payload.currentUserId
}
}
export default new Vuex.Store({
state,
mutations,
getters,
actions
})
#/vuex/login/mutation_types
export const IS_AUTHENTICATED = 'IS_AUTHENTICATED'
export const CURRENT_USER_ID = 'CURRENT_USER_ID'

You have already created a store .
In your login module you just need to export the object no need to create a new store and export it
so in your login module change the export statement to just export a plain object
export default {
state,
mutations,
getters,
actions
}

...mapGetters('login', ['isAuthenticated']})
you should also specify the module

Related

Vue3 Pinia Store cannot access 'store" before initializsation

I have a UserStore which contains some information about the current user. This store also is responsible for loggin in and out.
In order to make the getters available I map the getters to my computed attribute within my Vue component.
Unfortunately I get an error saying that it cannot access useUserStore before initilization.
This is my component:
<template>
//...
</template>
<script>
import {mapState} from "pinia"
import {useUserStore} from "../../stores/UserStore.js";
import LoginForm from "../../components/forms/LoginForm.vue";
export default {
name: "Login",
components: {LoginForm},
computed: {
...mapState(useUserStore, ["user", "isAuthenticated"]) //commenting this out makes it work
}
}
</script>
This is my store:
import { defineStore } from 'pinia'
import {gameApi} from "../plugins/gameApi.js"
import {router} from "../router.js";
export const useUserStore = defineStore("UserStore", {
persist: true,
state: () => ({
authenticated: false,
_user: null
}),
getters: {
user: (state) => state._user,
isAuthenticated: (state) => state.authenticated
},
actions: {
async checkLoginState() {
// ...
},
async loginUser(fields) {
// ...
},
async logutUser() {
// ...
}
}
})
And my main.js
import {createApp} from 'vue'
import App from './App.vue'
import gameApi from './plugins/gameApi'
import {router} from './router.js'
import store from "./stores/index.js";
createApp(App)
.use(store)
.use(router)
.use(gameApi)
.mount('#app')
And finally my store configuration:
import {createPinia} from "pinia"
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import {useUserStore} from "./UserStore.js";
const piniaStore = createPinia()
piniaStore.use(piniaPluginPersistedstate)
export default {
install: (app, options) => {
app.use(piniaStore)
const userStore = useUserStore()
const gameStore = useGameStore()
}
}
I wasn't able to initialize the store using the "old" way comparable with Vuex. Instead I had to make use of the setup() function Pinia Docu:
<script>
import {useUserStore} from "../../stores/UserStore.js";
export default {
name: "RegisterForm",
setup() {
// initialize the store
const userStore = useUserStore()
return {userStore}
},
data() {
return {
// ...
}
},
methods: {
checkLoginState() {
this.userStore.checkLoginState()
}
}
}
</script>

How to call a namespaced Vuex action in Nuxt

I am building an app using NUXT js. I am also using the store module mode because using classic mode returned some depreciation issue.
The PROBLEM is I get [vuex] unknown mutation type: mobilenav/showmobilenav error in my console.
so below are my stores
store/index.js
export const state = () => ({
})
export const mutations = ({
})
export const actions = ({
})
export const getters = ({
})
store/mobilenav.js
export const state = () => ({
mobilenav: false
})
export const mutations = () => ({
showmobilenav(state) {
state.mobilenav = true;
},
hidemobilenav(state) {
state.mobilenav = false;
}
})
export const getters = () => ({
ismobilenavvisible(state) {
return state.dropdown;
}
})
the VUE file that calls the mutation
<template>
<div class="bb" #click="showsidenav">
<img src="~/assets/svg/burgerbar.svg" alt="" />
</div>
</template>
<script>
export default {
methods: {
showsidenav() {
this.$store.commit("mobilenav/showmobilenav");
console.log("sidenav shown");
},
},
}
</script>
<style scoped>
</style>
Here is a more detailed example on how to write it.
/store/modules/custom_module.js
const state = () => ({
test: 'default test'
})
const mutations = {
SET_TEST: (state, newName) => {
state.test = newName
},
}
const actions = {
actionSetTest({ commit }, newName) {
commit('SET_TEST', newName)
},
}
export const myCustomModule = {
namespaced: true,
state,
mutations,
actions,
}
/store/index.js
import { myCustomModule } from './modules/custom_module'
export default {
modules: {
'custom': myCustomModule,
},
}
/pages/test.vue
<template>
<div>
<button #click="actionSetTest('updated test')">Test the vuex action</button>
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default {
methods: {
...mapActions('custom', ['actionSetTest']),
}
</script>

How to ensure that my vuex store is available in router

Good day am working on a vue project that uses both vue-router and vuex
In the app whenever login is successful, I store the the JWT token in vuex. However im strugling with the following
1) Dynamically show/hide login/logoff buttons in my navbar
2) Access JWT token in vuex store when the routes changes in router.js
So basically i need access to my vuex store in my router and also how to update my navbar component when my vuex state changes (ie when login is successful)
Below is my code. I'll start with main.js
import Vue from 'vue'
import Router from 'vue-router'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css'
import App from '../app.vue'
import router from '../router.js'
import store from '../store'
Vue.use(Router)
Vue.use(Vuetify)
const vuetify = new Vuetify
document.addEventListener('DOMContentLoaded', () => {
new Vue({
el: '#app',
router,
vuetify,
store,
render: h => h(App)
})
})
Then router.js
import Router from 'vue-router'
import Index from './views/index.vue'
import Appointments from './views/appointments.vue'
import CreateAppointment from './views/createAppointment.vue'
import Login from './views/login.vue'
import store from './store'
const router = new Router({
routes: [
{
path: '/',
name: 'root',
component: Index
},
{
path: '/login',
name: 'login',
component: Login
},
{
path: '/index',
name: 'index',
component: Index
},
{
path: '/appointments',
name: 'appointments',
component: Appointments,
props: true,
meta: {
requiresAuth: true
}
},
{
path: '/create-appointment',
name: 'create-appointment',
component: CreateAppointment,
meta: {
requiresAuth: true
}
}
]
})
router.beforeEach((to, from, next) => {
if(to.matched.some(record => record.meta.requiresAuth)) {
if (store.getters.isLoggedIn) {
next()
return
}
next('/login')
} else {
next()
}
})
export default router
Then comes store which is index.js inside of store folder
import Vue from 'vue'
import Vuex from 'vuex'
import * as patient from './modules/patient.js'
import * as appointment from './modules/appointment.js'
import * as user from './modules/user.js'
import * as notification from './modules/notification.js'
Vue.use(Vuex)
export default new Vuex.Store({
state: {},
modules: {
user,
patient,
appointment,
notification
}
})
And lastly is my nabar.vue component which is nested in App.vue. Ps i have taken out navigation drawer and bottom navigation to lessen the code.
<template>
<nav>
<v-app-bar app dark>
<v-btn text rounded dark v-on:click='showDrawer = !showDrawer'>
<v-icon class='grey--text' left>menu</v-icon>
</v-btn>
<v-toolbar-title d-sm-none>
<span class='font-weight-light'>SASSA</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<!-- Log in button -->
<v-btn text rounded dark v-on:click='login' v-show='!isLoggedIn'>
<span>Login</span>
<v-icon class='grey--text' right>arrow_forward</v-icon>
</v-btn>
<!-- Log out button -->
<v-btn text rounded dark v-on:click='logout' v-show='isLoggedIn'>
<span>Logout</span>
<v-icon class='grey--text' right>exit_to_app</v-icon>
</v-btn>
</v-app-bar>
</nav>
</template>
<script>
import { mapState } from 'vuex'
export default {
data(){
return {
activeBtn: 1,
showDrawer: false,
links: [
{ title: 'Appointments', icon: 'calendar_today', link: '/appointments'},
{ title: 'New Appointment', icon: 'event', link: '/create-appointment'}
]
}
},
computed: {
isLoggedIn() {
var result = (this.$store.getters['isLoggedIn'] == "true")
return result
},
...mapState(['user'])
},
methods: {
logout() {
this.$store.dispatch('user/logout')
.then(() => {
this.$router.push('/login')
})
},
login() {
this.$router.push('/login')
}
}
}
</script>
This is where i try to access store in router
if (store.getters.isLoggedIn) {
Any help would be highly appreciated
Below is the user.js module
import AxiosService from '../services/AxiosService.js'
export const namespaced = true
export const state = {
status: '',
token: '',
user: {}
}
export const mutations = {
AUTH_SUCCESS(state, payload){
state.status = 'logged_on'
state.token = payload.token
state.user = payload.user
},
LOGG_OFF(state){
state.status = 'logged_off'
state.token = ''
state.user = {}
}
}
export const actions = {
login({commit, dispatch}, user){
return AxiosService.logon(user)
.then(response => {
const token = response.data.auth_token
const user = response.data.user
localStorage.setItem('token',token)
commit('AUTH_SUCCESS', {token, user})
})
.catch(error => {
const notification = {
type: 'error',
message: 'Invalid Credentials !!!'
}
dispatch('notification/add', notification, { root: true})
throw error
})
},
logout({commit}){
localStorage.setItem('token','')
commit('LOGG_OFF')
}
}
export const getters = {
isLoggedIn: state => !!state.token,
authStatus: state => state.status,
getUser: state => state.user
}
Hey all i managed to fix it.
Problem was because my modules are namepsaced i have to access like this
store.getters['user/isLoggedIn']
Instead of
store.user.isLoggedIn
Thanks again. However i still have the issue of the navbar not updating

unknown mutation type: isOpen/toggleSideBar [vuex]

i'm trying to toggle a sidebar in my nuxt project
I have this in my store;
export const state = () => ({
isOpen: false
})
export const mutations = {
toggleSideBar (state) {
state.isOpen = !state.isOpen
}
}
export const getters = {
getDrawerState(state) {
return state.isOpen
}
}
in my navbar that contains the button, I have this:
import{mapMutations, mapGetters} from 'vuex'
...mapMutations({
toggleSideBar: "isOpen/toggleSideBar"
})
I have this is my default that where my class will be toggled:
import {mapGetters, mapMutations} from 'vuex'
computed:{
...mapGetters({
isOpen:'isOpen/getDrawerState'
})
}

vuex unknown local mutation type: updateValue, global type: app/updateValue. Mutations don't work

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.