Computed property not updaing UI in different tabs - vue.js

I use vuex for state management and authenticate users with
firebase
I use vuex-persisted state to save the state in cookies
In my vuex store I manage my userdata(user name , logged in status) as below
my store.js
/all imports here
export const store = new Vuex.Store({
state : {
user: {
loggedIn: false,
userName: 'Guest'
}
},
getters : {
g_user: state => {
return state.user;
}
},
mutations : {
m_logInUser: (state, userName) => {
state.user.loggedIn = true;
state.user.userName = userName;
},
m_loggedOut: (state) => {
state.user.loggedIn = false;
state.user.userName = 'Guest';
}
},
actions : {
a_logInUser: ({state, commit}, userInput) => {
//call to the API and on success commit m_logInUser mutation
},
a_loggedOut: ({commit}) => {
//call to the API and on success commit m_loggedOut mutation
}
},
plugins: [
createPersistedState({
paths: ['authStore.user'],
getState: (key) => Cookie.getJSON(key),
setState: (key, state) => Cookie.set(key, state, { expires: 3, secure: false })
})
]
});
now the problem I am facing
when I open the app in two different tabs and login the user in 1st tab , the user logs in which now hides the login buton and shows logout button and shows the username
but in the 2nd tab its still showing the login button , but when i console.log for the user, it shows logged in and also the username
here is my header.vue component
<template>
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<router-link to="/" tag="a" class="navbar-brand">Brand</router-link>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li #click="testmethod"><a>cuser</a></li>
<router-link to="/statuses" active-class="active" tag="li"><a>Status</a></router-link>
<router-link to="/users" active-class="active" tag="li"><a>Users</a></router-link>
</ul>
<ul class="nav navbar-nav navbar-right" v-if="!g_user.loggedIn">
<router-link to="/signup" active-class="active" tag="li"><a>Signup</a></router-link>
<router-link :to="{name: 'login'}" active-class="active" tag="li"><a>Login</a></router-link>
</ul>
<ul class="nav navbar-nav navbar-right" v-else>
<router-link to="/post" tag="li"><a><button class="btn btn-info">POST</button></a></router-link>
<router-link to="/current" active-class="active" tag="li"><a>{{ g_user.userName }}</a></router-link>
<li #click="logOut"><a>Log out</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
</template>
<script>
import { mapGetters } from 'vuex'
export default{
methods: {
logOut(){
this.$store.dispatch('a_loggedOut');
},
testmethod(){
var user = this.$firebase.auth().currentUser;
console.log(user);
console.log(user.email);
}
},
computed: {
...mapGetters([
'g_user'
])
}
}
</script>

You can watch your Vuex state:
mounted() {
this.$store.watch(
function(state) {
return state.g_user.loggedIn;
},
function(value) {
if (value === true) {
console.log('user is logged in');
// do your logic here, setup loggedin behavior hooks. For example call login() function which should first check if the user is already logged in
}
},
{
immediate: true
}
);
},

Related

Issue with nuxt/auth

For auth I do use nuxt-auth, when the login is successful, I want to redirect to the main page using this.$router.push('/'), then I get a response like blank page with the following message
2021
,
// for login
<template>
<div class="limiter">
<div
class="container-login100"
:style="{
backgroundImage: 'url(' + require(`#/assets/login/images/bg-01.jpg`) + ')',
}"
>
<div class="wrap-login100 p-l-110 p-r-110 p-t-62 p-b-33">
<form class="login100-form validate-form flex-sb flex-w">
<span class="login100-form-title p-b-53"> Login Admin </span>
<a href="facebook.com" class="btn-face m-b-20">
<i class="fa fa-facebook-official"></i>
Facebook
</a>
<a href="google.com" class="btn-google m-b-20">
<img :src="require(`#/assets/login/images/icons/icon-google.png`)" alt="GOOGLE" />
Google
</a>
<div class="p-t-31 p-b-9">
<span class="txt1"> Email </span>
</div>
<div class="wrap-input100 validate-input" data-validate="Email is required">
<input v-model="auth.email" class="input100" type="email" name="email" />
<span class="focus-input100"></span>
</div>
<div class="p-t-13 p-b-9">
<span class="txt1"> Password </span>
Forgot?
</div>
<div class="wrap-input100 validate-input" data-validate="Password is required">
<input v-model="auth.password" class="input100" type="password" name="pass" />
<span class="focus-input100"></span>
</div>
<div class="container-login100-form-btn m-t-17">
Login
</div>
<div class="w-full text-center p-t-55">
<span class="txt2"> Not a member? </span>
Register now
</div>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
auth: false,
data() {
return {
auth: {
email: null,
password: null,
},
}
},
mounted() {
if (this.$auth.loggedIn) {
this.$router.push('/')
}
},
methods: {
async submit() {
try {
const response = await this.$auth.loginWith('local', { data: this.auth })
this.$router.push('/')
} catch (err) {
console.log(err)
}
},
},
}
</script>
store vuex index.js
export const getters = {
isAuthenticated(state) {
return state.auth.loggedIn
},
loggedInUser(state) {
return state.auth.user
}}
}
layout default.vue
<template>
<div class="wrapper">
<Sidebar v-if="isAuthenticated" />
<div :class="isAuthenticated ? 'main-panel' : ''">
<Navbar v-if="isAuthenticated" />
<Nuxt />
<Footer v-if="isAuthenticated" />
</div>
</div>
</template>
<script>
import Sidebar from '#/components/layout/Sidebar.vue'
import Navbar from '#/components/layout/Navbar.vue'
import Footer from '#/components/layout/Footer.vue'
import { mapGetters } from 'vuex'
export default {
components: { Sidebar, Navbar, Footer },
computed: {
...mapGetters(['isAuthenticated', 'loggedInUser']),
},
}
</script>
// auth nuxt config
auth : {
strategies: {
local: {
token: {
property: 'token',
required: true,
type: 'Bearer'
},
user: {
property: 'user',
autoFetch: true
},
endpoints: {
login: { url: '/sign/login', method: 'post' },
logout: { url: '/sign/logout', method: 'post' },
user: { url: '/sign/user-login', method: 'get' }
}
}
}
}
base index ('/')
<template>
<div class="container">
<div>
<Logo />
<h1 class="title">Learn Nuxt</h1>
<div class="links">
<a href="https://nuxtjs.org/" target="_blank" rel="noopener noreferrer" class="button--green">
Documentation
</a>
<a
href="https://github.com/nuxt/nuxt.js"
target="_blank"
rel="noopener noreferrer"
class="button--grey"
>
GitHub
</a>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['isAuthenticated', 'loggedInUser']),
},
}
</script>
In your vuex store, the state parameter in your getter only has access to local state. You can't access the auth state the way you tried.
In a vuex module, a getter gets 4 arguments, namely local state, local getters, root state and root getters. So if you would rewrite your getters like this it would probably work:
export const getters = {
isAuthenticated(state, getters, rootState) {
return rootState.auth.loggedIn
},
loggedInUser(state, getters, rootState) {
return rootState.auth.user
}}
}
But I still think it is a bit redundant doing it like that. I would replace isAuthenticated with this.$auth.loggedIn in your default layout. The nuxt-auth module globally injects the $auth instance, meaning that you can access it anywhere using this.$auth.
I had same problem after authorizing user and redirect user to the home page.
After many tries and doing many works, the right config of auth in nuxt.config.js seemed like this:
auth: {
strategies: {
local: {
scheme: 'refresh',
token: {
property: 'access_token',
tokenType: false,
maxAge: 60 * 60
},
refreshToken: {
property: 'refresh_token',
data: '',
maxAge: 60 * 60
},
endpoints: {
login: {
url: 'url/of/token',
method: 'urlMethod'
},
refresh: {
url: 'url/of/refreshToken',
method: 'urlMethod'
},
logout: false,
user: false
}
}
},
cookie: false,
redirect: {
login: '/login/page',
logout: '/login/page',
callback: false,
home: '/home/page'
}
},
Note that I didn't have any refreshToken property, but I should set it as empty string in config to be able to work with nuxt/auth!
Hope I could help

Nuxtjs component not hotreloading on v-if condition change

I have a navbar component that contains 2 buttons "Logout" and "Dashboard" that are rendered only if the user is authenticated. (using v-if).
However when the user click on the Logout button, the navbar is not reloaded and so the buttons stay visible until you reload the page. How do I set my v-if condition so that when the value of the v-if change, also the components (divs) update?
My Navbar
<!-- ... -->
<template slot="end">
<b-navbar-item tag="div">
<div class="buttons">
<a v-if="userToken" class="button is-primary" style='padding:4px; margin-right:0;'>
<b-button size="is-normal"
type="is-primary"
icon-right="close"
outlined
inverted
style="margin-bottom: 0;"
#click="logout">
Logout
</b-button>
</a>
<a v-if="userToken" class="button is-primary" style='padding:4px; margin-left:0;'>
<b-button size="is-normal"
type="is-success"
icon-right="account-circle-outline"
style="margin-bottom: 0;"
#click="goToDashboard">
Dashboard
</b-button>
</a>
<a v-else class="button is-primary">
<b-icon icon="account-circle-outline" size="is-medium"> </b-icon>
</a>
</div>
</b-navbar-item>
</template>
</b-navbar>
</template>
<script>
export default {
data () {
return {
userToken : '',
},
methods: {
getUserToken() {
let token = this.$cookies.get('userToken')
if (token) {
this.userToken = token
this.$router.push('user_dashboard')
}
},
logout() {
this.$cookies.remove('userToken')
this.userToken = ''
}
},
mounted() {
this.getUserToken()
},
// ...
</script>
Ok I'm really sorry, I eventually solved the issue by making those changes:
methods: {
getUserToken() {
let token = this.$cookies.get('userToken')
if (token) {
this.userToken = token
this.$router.push('user_dashboard')
} else {
this.userToken = ''
}
},
logout() {
this.$cookies.remove('userToken')
this.getUserToken()
}
},
mounted() {
this.getUserToken()
},

v-if with computed property is triggering just after reload of page

After Login my menu has to change "connexion" to "deconnexion" but it not happening.
I have :
LoginPage.vue :
<fg-input addon-left-icon="now-ui-icons ui-1_email-85"
v-model="email"
placeholder="Email">
</fg-input>
<fg-input addon-left-icon="now-ui-icons text_caps-small"
v-model="password"
placeholder="Mot de passe">
</fg-input>
<template slot="raw-content">
<div class="card-footer text-center">
<a href="#pablo" class="btn btn-primary btn-round btn-lg btn-block"
#click.prevent="login">Se connecter</a>
</div>
....
<script>
....
methods: {
...mapActions(['loginUser']),
login(){
if (this.email.length > 0 && this.password.length > 0) {
this.loginUser({
email: this.email,
password: this.password,
})
} else {
this.password = ""
return alert("Passwords do not match")
}
....
Store.js :
state:{
user: {},
token : "",
loggedIn: false
},
getters:{
user : state =>{ return state.user },
token : state =>{ return state.token },
},
mutations:{
SET_USER:(state,newValue)=>{
return state.user = newValue
},
SET_TOKEN:(state,newValue)=>{
return state.token = newValue
},
SET_LOGGEDIN:(state, newValue)=>{
return state.loggedIn = newValue
},
},
actions:{
loginUser({commit,state}, payload){
axios.post("http://127.0.0.1:8000/api/auth/login", payload)
.then(response => {
commit('SET_TOKEN', JSON.stringify(response.data.accessToken))
commit('SET_LOGGEDIN', true)
router.push({name:'accueil'})
})
.catch(error => {
console.log(error);
});
},
StarterNavbar.vue :
<template v-if="loggedIn" >
<li class="nav-item nav-link" style="cursor: pointer;" #click.prevent="logoutUser">
Deconnexion
</li>
</template>
<template v-else>
<router-link class="nav-item nav-link" to="/register">
Inscription
</router-link>
<router-link class="nav-item nav-link" to="/login">
Connexion
</router-link>
</template>
....
computed:{
...mapState(['loggedIn', 'token']),
},
After Login action the router push to home page (Accueil.vue).
In this component I trace 'loggedIn' and 'token' properties of Store.
mounted() {
console.log('token',this.token) // exist
console.log('loggedIn', this.loggedIn) // true
But I don't see the changes in my StarterNavbar : Connexion is displayed instead Deconnexion.
Why my menu is changing just after reload of page?
In Vue-devtools after Login action:
switch to Components :
vuex bindings
loggedIn: true
token: ""eyJ0eXAiOiJKV1QiLCJ...""
switch to Vuex:
mutation
payload:true
type:"SET_LOGGEDIN"
but STATE is grey, params have old value... and on bottom the following is written:
Recording state on demande.... displaying last received state and button "Load state". If I click on it State become black and params have new values.. the page not updated.
What does it mean and what happens?

Vuex state & getters not retrieving store data in Vue cli 3

Working with Vue cli 3 and vuex for the first time and trying to get a simple login working with vuex but the getters I have setup are not getting any data from the vuex store and just return nothing even with a state change.
Here's my store.ts:
export default new Vuex.Store({
state: {
loggedIn: true,
email: 'test',
password: 'test',
},
getters: {
loggedIn(state) {
return state.loggedIn;
},
userEmail(state) {
return state.email;
},
},
mutations: {
login(state, user) {
state.loggedIn = true;
state.email = user.e;
state.password = user.p;
},
logout(state) {
state.loggedIn = false;
},
},
actions: {
login(context, data) {
context.commit('login', data);
},
logout(context) {
context.commit('logout');
},
},
});
and the navbar.vue where im trying to change the nav based on if a user is logged in.
html
<template>
<nav id="nav" class="navbar fixed-top navbar-expand-md navbar-light bg-light">
<router-link to="/" class="navbar-brand">Navbar</router-link>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#topNav" aria-controls="topNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="topNav">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<router-link to="/" class="nav-link">Home</router-link>
</li>
<li class="nav-item" v-if="loggedIn">
<router-link to="/Dashboard" class="nav-link">Dashboard</router-link>
</li>
<li class="nav-item" v-else>
<router-link to="/Login" class="nav-link">Login</router-link>
</li>
<li class="nav-item">
<p class="nav-link">user : {{ test }}</p>
</li>
</ul>
</div>
</nav>
</template>
typescript
import { Component, Vue } from 'vue-property-decorator';
import { mapState, mapGetters } from 'vuex';
#Component({
computed: mapState(['loggedIn']),
})
#Component
export default class NavBar extends Vue {
private test: string = 'test';
constructor() {
super();
}
}

Vuejs 2 reload content by changing slug

I have the following code and it works by refreshing the page it works fine, but not when clicking on the nav.
How can i achieve that when i click on users, the method fetchData will be executed? So i can see the console.log and the json data.
Navigation:
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<router-link v-for="item in items" v-if="item.navitems && item.navitems.navitem" :to="item.navitems.navitem.url" tag="li" active-class="active" exact><a>{{item.navitems.navitem.name}}</a></router-link>
Content.vue
<template>
<div>
<div class="container">
<h1>{{ slug }}</h1>
<ul v-if="items && items.length">
<li v-for="item of items">
<p><strong>{{item.name}}</strong></p>
<p>{{item.email}}</p>
</li>
</ul>
</div>
</div>
</template>
<script>
export default{
props: ['slug'],
data: () => ({
items: []
}),
created: function () {
this.fetchData();
},
methods: {
fetchData: function () {
var self = this;
console.log(this.slug);
$.get( 'http://jsonplaceholder.typicode.com/' + this.slug, function( data ) {
self.items = data;
console.log(data);
});
}
}
}
</script>
Changed created into mounted and add watch.
watch: {
'$route.params.slug'(newSlug, oldSlug) {
this.fetchData(newId);
}
}