I am building a frontend app using Vue, Vuex, and Vue Router. My goals is to access authentication module imported from the different JS file so that it can change logged in state and save JWT token on frontend. But the problem is I am getting an error ncaught TypeError: Cannot read properties of undefined (reading '$store')
when submitting the user registration request and try to access the state store.
My project currently
My app’s dependencies section package.json
are:
"dependencies": {
"@vuelidate/core": "^2.0.0-alpha.34",
"@vuelidate/validators": "^2.0.0-alpha.26",
"axios": "^0.25.0",
"core-js": "^3.6.5",
"primeflex": "^3.1.2",
"primeicons": "^5.0.0",
"primevue": "^3.10.0",
"vue": "^3.0.0",
"vue-router": "^4.0.0-0",
"vuex": "^4.0.0-0"
},
My project structure so far is:
Project frontend structure
My main.js
:
import { createApp } from 'vue'
import App from './App.vue'
import PrimeVue from 'primevue/config';
import router from './router'
import store from './store'
import "primevue/resources/themes/saga-blue/theme.css";
import "primevue/resources/primevue.min.css";
import "primeicons/primeicons.css";
import Button from "primevue/button";
import Dialog from 'primevue/dialog';
import InputText from 'primevue/inputtext';
import Password from 'primevue/password';
import Divider from 'primevue/divider';
const app = createApp(App);
app.use(store)
app.use(router)
app.use(PrimeVue)
app.component("Button", Button)
app.component("Dialog", Dialog)
app.component("InputText", InputText)
app.component("Password", Password)
app.component("Divider", Divider)
app.mount('#app')
My store/index.js
import { createStore } from 'vuex'
import { auth } from "./auth.module";
export default createStore({
state: {
},
mutations: {
},
actions: {
},
modules: {
auth
}
})
My auth.module.js
:
import AuthService from '../services/auth.service';
const user = JSON.parse(localStorage.getItem('user'));
const initialState = user
? { status: { loggedIn: true }, user }
: { status: { loggedIn: false }, user: null };
export const auth = {
namespaced: true,
state: initialState,
actions: {
login({ commit }, user) {
return AuthService.login(user).then(
user => {
commit('loginSuccess', user);
return Promise.resolve(user);
},
error => {
commit('loginFailure');
return Promise.reject(error);
}
);
},
logout({ commit }) {
AuthService.logout();
commit('logout');
},
register({ commit }, user) {
return AuthService.register(user).then(
response => {
commit('registerSuccess');
return Promise.resolve(response.data);
},
error => {
commit('registerFailure');
return Promise.reject(error);
}
);
}
},
mutations: {
loginSuccess(state, user) {
state.status.loggedIn = true;
state.user = user;
},
loginFailure(state) {
state.status.loggedIn = false;
state.user = null;
},
logout(state) {
state.status.loggedIn = false;
state.user = null;
},
registerSuccess(state) {
state.status.loggedIn = false;
},
registerFailure(state) {
state.status.loggedIn = false;
}
}
};
My Register.vue
component:
<template>
<div >
<div class="form-demo">
<Dialog v-model:visible="showMessage" :breakpoints="{ '960px': '80vw' }" :style="{ width: '30vw' }" position="top">
<div class="flex align-items-center flex-column pt-6 px-3">
<i class="pi pi-check-circle" :style="{fontSize: '5rem', color: 'var(--green-500)' }"></i>
<h5>Registration Successful!</h5>
<p :style="{lineHeight: 1.5, textIndent: '1rem'}">
Your account is registered under name <b>{{state.name}}</b>
</p>
</div>
<template #footer>
<div class="flex justify-content-center">
<Button label="OK" @click="toggleDialog" class="p-button-text" />
</div>
</template>
</Dialog>
<div class="flex justify-content-center">
<div class="card">
<h5 class="text-center">Register</h5>
<form @submit.prevent="handleSubmit(!v$.$invalid)" class="p-fluid">
<div class="field">
<div class="p-float-label">
<InputText id="name" v-model="v$.name.$model" :class="{'p-invalid':v$.name.$invalid && submitted}" />
<label for="name" :class="{'p-error':v$.name.$invalid && submitted}">Name*</label>
</div>
<small v-if="(v$.name.$invalid && submitted) || v$.name.$pending.$response" class="p-error">{{v$.name.required.$message.replace('Value', 'Name')}}</small>
</div>
<div class="field">
<div class="p-float-label">
<Password id="password" v-model="v$.password.$model" :class="{'p-invalid':v$.password.$invalid && submitted}" toggleMask>
<template #header>
<h6>Pick a password</h6>
</template>
<template #footer="sp">
{{sp.level}}
<Divider />
<p class="mt-2">Suggestions</p>
<ul class="pl-2 ml-2 mt-0" style="line-height: 1.5">
<li>At least one lowercase</li>
<li>At least one uppercase</li>
<li>At least one numeric</li>
<li>Minimum 8 characters</li>
</ul>
</template>
</Password>
<label for="password" :class="{'p-error':v$.password.$invalid && submitted}">Password*</label>
</div>
<small v-if="(v$.password.$invalid && submitted) || v$.password.$pending.$response" class="p-error">{{v$.password.required.$message.replace('Value', 'Password')}}</small>
</div>
<Button type="submit" label="Submit" class="mt-2" />
</form>
</div>
</div>
</div>
</div>
</template>
<script>
import { reactive, ref } from 'vue';
import { required } from "@vuelidate/validators";
import { useVuelidate } from "@vuelidate/core";
import '/node_modules/primeflex/primeflex.css';
import User from '../models/user';
export default {
name: "Register",
setup() {
const state = reactive({
name: '',
password: '',
});
const rules = {
name: { required },
password: { required },
};
const submitted = ref(false);
const showMessage = ref(false);
const v$ = useVuelidate(rules, state);
const handleSubmit = (isFormValid) => {
submitted.value = true;
if (!isFormValid) {
return;
} else {
let user = new User('', '');
user.username = state.name;
user.password = state.password;
this.$store.dispatch('auth/register', this.user).then(
data => {
this.message = data.message;
// this.successful = true;
},
error => {
this.message =
(error.response && error.response.data) ||
error.message ||
error.toString();
// this.successful = false;
}
);
}
toggleDialog();
}
const toggleDialog = () => {
showMessage.value = !showMessage.value;
if(!showMessage.value) {
resetForm();
}
}
const resetForm = () => {
state.name = '';
state.password = '';
submitted.value = false;
}
return { state, v$, handleSubmit, toggleDialog, submitted, showMessage }
}
}
</script>
I followed this JWT with Vue and Spring tutorial for authentication logic and PrimeVue validation for Register component but tweaked to fit my project API spec.
Problem
When I hit submit user registration button, I get an error from runtime-core.esm-bundler.js?5c40:218:
Uncaught TypeError: Cannot read properties of undefined (reading '$store')
at Proxy.handleSubmit (Register.vue?73cf:105:1)
at eval (Register.vue?73cf:22:1)
at eval (runtime-dom.esm-bundler.js?830f:1463:1)
at callWithErrorHandling (runtime-core.esm-bundler.js?5c40:155:1)
at callWithAsyncErrorHandling (runtime-core.esm-bundler.js?5c40:164:1)
at HTMLFormElement.invoker (runtime-dom.esm-bundler.js?830f:363:1)
I created my project using Vue CLI command vue create frontend with manual setting. I included Vuex and Vue Router in the initial setup.
Attempt tried so far
I rolled back my store
directory to initial version which is almost the same as what vue cli vue create command initialised for me. I deleted my store/auth.module.js
and just inserted one line in store/index.js
:
import { createStore } from 'vuex'
import { auth } from "./auth.module";
export default createStore({
state: {
count: 0
},
mutations: {
},
actions: {
},
modules: {
}
})
Then, inside handleSubmit function in Register.vue script, I called console.log(this.$store.state.count)
. I still got the same error I stated in the Problem section above.
Can anyone help me fix this? Thank you.