0
votes

I have a django-rest-axios-vuejs application stack for which I'm trying to do something in the vue-router.

In the vue-router beforeEach guard, I'm verifying permissions and it is done by checking something in an object called me in vuex store. Everything works well except when I refresh the page.

Indeed refreshing the page also clears vuex store and my beforeEach tries to check the me object from the store which is empty.

Therefore I'd like to fetch this me object from the API if it isn't in the store. The problem is that it takes "some time" and the hasPermission() method executes before the API call is finished.

So I tried to put a await keyword before my API call but it doesn't work.

My beforeEach guard :

router.beforeEach(async (to, from, next) => {
  const isLoggedIn = getIsLoggedIn()
  handleLoggedInStatus(isLoggedIn)
  if (to.meta.requiresAuth) {
    if (isLoggedIn) {
      if (to.meta.permission) {
        if (!store.state.me) await store.dispatch('FETCH_ME')
        hasPermission(to.meta.permission) ? next() : next({ name: 'HomePage' })
      } else {
        next()
      }
    } else {
      next({ name: 'LoginForm' })
    }
  } else {
    next()
  }
})

My action from the store :

actions: {
    FETCH_ME: (state) => {
      http
        .get('base/users/me/')
        .then(response => {
          state.me = response.data
        })
        .catch(error => {
          console.log(error)
        })
    }
  }

The only way I've found to make it wait is to do the following :

function sleep (ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}

router.beforeEach(async (to, from, next) => {
  const isLoggedIn = getIsLoggedIn()
  handleLoggedInStatus(isLoggedIn)
  if (to.meta.requiresAuth) {
    if (isLoggedIn) {
      if (to.meta.permission) {
        if (!store.state.me) {
          store.dispatch('FETCH_ME')
          await sleep(2000)
        }
        hasPermission(to.meta.permission) ? next() : next({ name: 'HomePage' })
      } else {
        next()
      }
    } else {
      next({ name: 'LoginForm' })
    }
  } else {
    next()
  }
})

To make it wait a "random" (2 seconds) amount of time with a little sleep() method.

I'm kinda new to the usage of async await so.. What am I missing to make await store.dispatch('FETCH_ME') work ?

Thanks in advance :)

2

2 Answers

0
votes

I have a pet project with similar logic. I am not using meta

async beforeEnter(to, from, next) {
   await store.dispatch('getUser')
   if (store.getters.user) return next()
    next('/login')
}

The logic is the following. If the user is logged in, there is a cookie in the browser and it's being sent with the store.dispatch. And if the token is valid, backend returns the user, ie if the getter returns the user, the user is logged in. I think your logic should be the same

0
votes

I finally found this LINK which I didn't see before...

This allowed me to rewrite my FETCH_ME action like this :

FETCH_ME ({ commit }) {
  return new Promise((resolve, reject) => {
    http
     .get('base/users/me/')
     .then(response => {
       commit('SET_ME', response.data)
       resolve()
     })
     .catch(error => {
       console.log(error)
       reject(error)
     })
   })
}

where SET_ME is a mutation I already had :

SET_ME: (state, user) => {
  state.me = user
},

Which is finally working for my case, where doing this in the router.beforeEach guard :

if (!store.state.me) await store.dispatch('FETCH_ME') effectively waits for the dispatch action to finish.