How to ensure that my vuex store is available in router - vue.js

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

Related

Storybook - Set and get Vuex state

I try to use Storybook in a Nuxt project. Story file looks similar to
import Chip from '~/components/UI/Chip.vue'
import store from '#/storybook/store';
export default {
title: 'Chips',
component: Chip,
}
const Template = (args, { argTypes }) => ({
store: store,
props: Object.keys(argTypes),
components: { Chip },
});
export const Primary = Template.bind({})
Primary.args = {
color: 'background-darken-4'
}
And Store
import Vue from 'vue'
import Vuex from 'vuex'
import themes from '~/components/PassporterUI/themes/index'
import ThemeCollection from '~/models/ThemeCollection'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
theme: undefined,
},
mutations: {
theme(state) {
const defaultTheme = themes.find(
(theme) => theme.name === 'passporter-light'
)
if (defaultTheme) {
state.theme = new ThemeCollection({
current: defaultTheme,
list: themes,
})
}
},
},
actions: {
setTheme({ commit }) {
commit('theme', state.theme)
},
},
})
Returns this multiple errors
Do, anyone knows what is the right way to fix this?

VueJs: cannot use router.push

So, i want to vue router.push on my store.js, but i keep getting error Cannot read property 'push' of undefined
i've tried to import vue-router in my store.js, but still in vain
here's my app.js :
import Vue from 'vue'
import VueRouter from 'vue-router'
//Import and install the VueRouter plugin with Vue.use()
Vue.use(VueRouter)
import App from './views/App'
import Home from './views/Home'
import Login from './views/Login.vue'
import Register from './views/Register.vue'
const router = new VueRouter({
mode: 'history',
routes: [{
path: '/',
name: 'home',
component: Home,
meta: { requiresAuth: true }
},
{
path: '/login',
name: 'login',
component: Login
},
{
path: '/register',
name: 'register',
component: Register
},
],
});
const app = new Vue({
el: '#app',
components: { App },
router,
});
and here's my store.js :
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
accessToken: null,
loggingIn: false,
loginError: null
},
mutations: {
loginStart: state => state.loggingIn = true,
loginStop: (state, errorMessage) => {
state.loggingIn = false;
state.loginError = errorMessage;
},
updateAccessToken: (state, accessToken) => {
state.accessToken = accessToken;
},
logout: (state) => {
state.accessToken = null;
}
},
actions: {
doLogin({ commit }, loginData) {
commit('loginStart');
console.log(loginData)
axios.post('http://localhost:8000/api/login', loginData)
.then(response => {
console.log(response)
let accessToken = response.data.jwt;
document.cookie = 'jwt_access_token=' + accessToken;
commit('updateAccessToken', accessToken);
///this caused the error
this.$router.push({ path: '/' })
})
.catch(error => {
// commit('loginStop', error.response.data.error);
console.log(error)
commit('updateAccessToken', null);
console.log(error.response);
})
}
}
})
as you can see, after i call doLogin() function, and using axios, it stop at the this.$router.push({ path: '/' }) line, causing error.
You need to make a router.js file
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const router = new Router({
...
})
export default router
In app.js replace the import of the vue-router to your new router.js and remove Vue.use(Router).
In the store, this is not the Vue instance.
Import the router.js in your store.js;
import router from './router'
Then you can access it like this;
router.push({ path: '/' })
I also noticed that you haven't add the store to the Vue instance. Import and add in app.js.
import store from './store'
...
const app = new Vue({
el: '#app',
components: { App },
router,
store //<<<<<<<<
});

Uncaught TypeError: Cannot read property '$router' of undefined

I am working on a firebase based vue app that should redirect users to home page after successfully logged in. I receive howewer Cannot read property '$router' of undefined error in the console once clicked on login.
I already tried to import router from "./router" on Login component but it didn't work.
main.js
import Vue from "vue";
import firebase from "firebase";
import App from "./App.vue";
import router from "./router";
import swalPlugin from './plugins/VueSweetalert2';
Vue.config.productionTip = false;
let app ='';
firebase.initializeApp( {
apiKey: "xx",
authDomain: "xx",
databaseURL: "xx",
projectId: "xx",
storageBucket: "xx",
messagingSenderId: "xx"
});
Vue.use(swalPlugin);
firebase.auth().onAuthStateChanged(() => {
if (!app) {
app = new Vue({
router,
render: h => h(App)
}).$mount("#app");
}
});
router.js
import firebase from 'firebase';
import Vue from "vue";
import Router from "vue-router";
import Home from "./views/Home.vue";
import Login from "./views/Login.vue";
import SignUp from "./views/SignUp.vue";
Vue.use(Router);
const router = new Router({
routes: [
{
path: "*",
redirect: "/login",
},
{
path: "/",
redirect: "/login",
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/sign-up',
name: 'SignUp',
component: SignUp
},
{
path: "/home",
name: "Home",
component: Home,
meta: {
requiresAuth: true
}
}
]
});
router.beforeEach ((to, from, next) => {
const currentUser = firebase.auth.currentUser;
const requiresAuth = to.matched.some (record => record.meta.requiresAuth);
if (requiresAuth && !currentUser) next('login');
else if (!requiresAuth && currentUser) next('home');
else next();
});
export default router;
Login.vue
<template>
<form autocomplete="off">
<div class="login">
<h1>Login</h1>
<p>Sign in to stay updated with the latest news</p>
<hr>
<input type="text" placeholder="Email" v-model="email">
<input type="password" placeholder="Enter Password" v-model="password">
<hr>
<button #click="login">Login</button>
<p>Don't have an account yet? Create one <router-link to="/sign-up">here</router-link></p>
</div>
</form>
</template>
<script>
import firebase from 'firebase';
import Swal from 'sweetalert2';
export default {
name: 'login',
data() {
return {
email: '',
password: ''
}
},
methods: {
login: function(){
firebase.auth().signInWithEmailAndPassword(this.email, this.password).then(
function (user) {
this.$router.replace('home')
},
function (err) {
Swal.fire({
type: 'error',
title: 'An error occurred...',
text: err.message
})
}
);
}
}
}
</script>
I think you lost your scope using function.
Solve it by using es6 syntax (if you can afford/using babel), (user) => { .. }.
Or by setting something like var _this = this; in the main login function and referencing that.

Camera mobile used for progressive web app

I realize a progressive application web app under view and I have a problem for the use of the mobile camera. I have a blank page. Here is my file seen for the camera:
<template>
<div class="container" id="app">
<router-link class="waves-effect waves-light btn" to="/livreur" #click.native="hideMenu"><i class="material-icons">arrow_back</i>
</router-link>
<div class="camera-modal">
<video ref="video" class="camera-stream"/>
</div>
</div>
</template>
<script>
var constraints = {
audio: false,
video: {
facingMode: {exact: 'environment'}
}
}
export default {
mounted () {
navigator.mediaDevices.getUserMedia(constraints)
.then(function (mediaStream) {
var video = document.querySelector('video')
video.srcObject = mediaStream
video.onloadedmetadata = function (e) {
video.play()
}
})
.catch(function (err) {
console.log(err.name + ': ' + err.message)
})
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
</style>
My file main.js:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
import Axios from 'axios'
import VueSignature from 'vue-signature-pad'
Vue.prototype.$http = Axios
const token = localStorage.getItem('user-token')
if (token) {
Vue.prototype.$http.defaults.headers.common['Authorization'] = token
}
Vue.use(VueSignature)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
My router.js file:
import Vue from 'vue'
import Router from 'vue-router'
import store from './store.js'
import Home from '#/components/Home'
import Delivered from '#/components/Delivered'
import Absent from '#/components/Absent'
import Refused from '#/components/Refused'
import Livreur from '#/components/preprations/Livreur'
import Prepa from '#/components/preprations/Prepa'
import Scan from '#/components/preprations/Scan'
import Login from '#/components/Login'
Vue.use(Router)
let router = new Router({
routes: [
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/delivered',
name: 'delivered',
component: Delivered
},
{
path: '/absent',
name: 'absent',
component: Absent
},
{
path: '/refused',
name: 'refused',
component: Refused
},
{
path: '/livreur',
name: 'livreur',
component: Livreur
},
{
path: '/prepa',
name: 'prepa',
component: Prepa
},
{
path: '/scan',
name: 'Scan',
component: Scan
}
]
})
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
I tried to change the constraints but nothing helps, I try the default values ​​but it does not work.
I do not see where it's blocking at all. Thank you for your help.

Vuex Modules issue with _mapGetters() : getters should be function

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