I'm creating a navbar that shows or hides buttons depending if the user is logged in or not. For that, I'm saving the state on Vuex and localStorage.
I'm trying to build a dynamic menu, using a list of objects (i.e. rightMenu
) that contains the information of the buttons (i.e. route, title and a flag that indicates if the button may show or not if the user is logged in).
Always that the user logs in the system, the this.$store.state.auth.isUserLoggedIn
changes to true
, however the template does not change, the button stays in the same initial state when the user was not logged in.
For example: the sign out
button does not show when this.$store.state.auth.isUserLoggedIn
updates.
But when I click 'ctrl+F5' and the page reloads, the buttons show correctly.
In this case, for example, the sign out
button appears correctly when I reload the page manually.
I'm thinking in to force the page to reload again when the user logs in or logs out, however I believe that it is not a good option.
Could anyone help me?
I'm giving the code that I'm using below.
Thank you in advance.
Menu.vue > template
<div>
<v-toolbar color='grey darken-3' dark>
<v-toolbar-title>Site</v-toolbar-title>
...
<v-toolbar-items class='hidden-sm-and-down'>
<v-btn v-for='item in rightMenu' :key='item.title'
:to='item.to' v-if='item.showButton' flat>
{{ item.title }}
</v-btn>
</v-toolbar-items>
</v-toolbar>
<router-view/>
</div>
Menu.vue > script
export default {
data () {
return {
rightMenu: [
{ to: '/sign_in', title: 'sign in'
showButton: !this.$store.state.auth.isUserLoggedIn },
{ to: '/sign_up', title: 'sign up'
showButton: !this.$store.state.auth.isUserLoggedIn },
{ to: '/sign_out', title: 'sign out'
showButton: this.$store.state.auth.isUserLoggedIn }
]
}
},
...
}
store.js
const store = new Vuex.Store({
state: {
auth: {
token: '',
isUserLoggedIn: false
}
},
mutations: {
setAuthToken (state, token) { // I use it on the Login
state.auth.token = token
state.auth.isUserLoggedIn = !!token
localStorage.setItem('store', JSON.stringify(state))
},
cleanAuth (state) { // I use it on the Logout
state.auth = {
token: '',
isUserLoggedIn: false
}
localStorage.setItem('store', JSON.stringify(state))
}
}
...
})
EDIT 1:
When I use this.$store.state.auth.isUserLoggedIn
explicitly on my code, it works well. So, the button appears and disappears correctly. I give below an example:
Menu.vue > template
<v-toolbar-items class='hidden-sm-and-down'>
<v-btn v-if='this.$store.state.auth.isUserLoggedIn' flat>
Test {{ this.$store.state.auth.isUserLoggedIn }}
</v-btn>
</v-toolbar-items>
Hence, I believe that the problem is in the binding of showButton
with this.$store.state.auth.isUserLoggedIn
.
Through the answers of Chris Li, Andrei Gheorghiu and Sajib Khan I could solve my problem.
Andrei Gheorghiu have explained that I can't access computed properties in data()
and Chris Li suggested that I use a computed variable instead. These answers plus Sajib Khan example I was able to think in a solution that I share below. I hope that it help others in the future.
In a nutshell, I've created a computed property that returns my array and always when this.$store.state.auth.isUserLoggedIn
updates, the array changes together (consequently the menu as well).
I intent to create a mapGetter to my this.$store.state.auth.isUserLoggedIn
. As soon as I do it, I update the answer.
Thank you guys so much.
<script>
export default {
data () {
return { ... }
},
computed: {
rightMenu () {
return [
{ title: 'sign_in', to: '/sign_in',
showButton: !this.$store.state.auth.isUserLoggedIn },
{ title: 'sign_up', to: '/sign_up',
showButton: !this.$store.state.auth.isUserLoggedIn },
{ title: 'sign_out', to: '/sign_out',
showButton: this.$store.state.auth.isUserLoggedIn }
]
}
}
}
</script>
EDIT 1: Solution using mapGetters
Menu.vue
<script>
import { mapGetters } from 'vuex'
export default {
data () {
return { ... }
},
computed: {
...mapGetters([
'isUserLoggedIn'
]),
rightMenu () {
return [
{ title: 'sign_in', to: '/sign_in',
showButton: !this.$store.state.auth.isUserLoggedIn },
{ title: 'sign_up', to: '/sign_up',
showButton: !this.$store.state.auth.isUserLoggedIn },
{ title: 'sign_out', to: '/sign_out',
showButton: this.$store.state.auth.isUserLoggedIn }
]
}
}
}
</script>
store.js
I've added the following getter:
...
getters: {
isUserLoggedIn (state) {
return state.auth.isUserLoggedIn
}
}
...