CodeSpent
CodeSpent

Reputation: 1904

Vuex store/navigation guard doesn't work first time

I have a navigation guard on a user's dashboard which requires a user to exist to proceed.

export default new Router({
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/dashboard',
      name: 'dashboard',
      component: Dashboard,
      beforeEnter: (to, from, next) => {
        const user = JSON.parse(store.state.authenticatedUser)
        if(user) {
          console.log("Route accepted.")
          next()
        } else {
          console.log("Route rejected.")
          next('/')
        }
      }
    }
  ]
})

On my login form, I dispatch to obtainToken which returns the token and associated user.

<template>
  <v-container>
    <h1 class="display-2">Login</h1>
    <v-form 
      ref="form" 
      v-model="valid" 
      lazy-validation
    >
      <v-col cols="12" md="12">
        <v-row align="center">
          <v-text-field 
            v-model="email" 
            :rules=emailRules 
            label="Email" 
            required 
            dense
          ></v-text-field>
        </v-row>
        <v-row align="center">
          <v-text-field 
            v-model="password"
            label="Password"
            required
            dense
            type="password"
          ></v-text-field>
        </v-row>
      </v-col>
      <v-btn
        @click="login"
      >Login</v-btn>
    </v-form>
  </v-container>
</template>

<script>
export default {
  data: () => ({
    valid: true,
    password: '',
    email: '',
    emailRules: [
      v => !!v || 'Email is required.',
      v => /.+@.+\..+/.test(v) || 'You have entered an invalid email.',
    ],
  }),
  methods: {
    login() {
      this.$store.dispatch('obtainToken', { email: this.email, password: this.password })
      this.$router.push('dashboard')
    }
  }
}
</script>
  actions: {
    logout() {
      this.commit('revokeUser')
      this.commit('revokeToken')
    },
    obtainToken(random, payload) {
      const data = {
        email: payload.email,
        password: payload.password
      }

      api.post(this.state.endpoints.obtainToken, data)
      .then((response => {
        this.commit('updateSessionUser', response.data.user)
        this.commit('updateToken', response.data.token)
      }))
      .catch((e) => {
        console.log(e)
      })
    },

When clicking submit, the user is obtained, but the route is rejected because at the time a user doesn't exist. I have to click submit a second time which will allow the user to access the dashboard route.

enter image description here

So what I can gather from the output is that the $router.push is occurring as obtainToken is occurring so they race. Logging in a second time there is already a user present, so it proceeds. What am I doing wrong here?


I have as well tried to make it so router.push() is called as resolution to the promise as well.

login() {
      this.$store.dispatch('obtainToken', { email: this.email, password: this.password })
      .then(() =>
           this.$router.push('dashboard')
      )
}

Upvotes: 2

Views: 541

Answers (2)

LB_19
LB_19

Reputation: 41

The issue appears to be in the login method.

login() {
      this.$store.dispatch('obtainToken', { email: this.email, password: this.password })
      this.$router.push('dashboard')
}

Will be running both calls simultaneously. Attempting to navigate the user before the result of the obtain token call.

login() {
      this.$store.dispatch('obtainToken', { email: this.email, password: this.password })
      .then(() =>
           this.$router.push('dashboard')
      )
}

Once the token exists, it can be fetched from state when navigating to the dashboard.

Upvotes: 2

Diamond
Diamond

Reputation: 3428

Calling dispatch is async.

When changing the router, it is undefined yet.

So you need to call dispatch in mounted() before to move another page.

mounted() {
  this.$store.dispatch('obtainToken', { email: this.email, password: this.password })
},
methods: {
  login() {
    this.$router.push('dashboard')
  }
}

Or

methods: {
  login() {
    this.$store.dispatch('obtainToken', { email: this.email, password: this.password }).then(() => this.$router.push('dashboard'))
  }
}

EDIT: You need to return promise in obtainToken like below;

obtainToken(random, payload) {
  ...
  return api.post(this.state.endpoints.obtainToken, data) // just add `return`
  ...
}

Upvotes: 1

Related Questions