2

So, I'm trying to make a request with axios in my main.js file.

I'm using, as shown, vue-router to make this request before each component is loaded. However, I'm not able to get this to work when my webpage is loaded for the first time. I mean, axios request is done after the component is loaded. Then, this is going to fail:

mounted() {
    if (Vue.prototype.$user.role == "Owner") {
      this.isOwner = true;
      this.estancoId = Vue.prototype.$user.estanco;
    }
  },

It shows me this error on the console log:

[Vue warn]: Error in mounted hook: "TypeError: Cannot read property 'role' of undefined"

found in

---> <Header> at src/components/Header.vue
       <App> at src/App.vue
         <Root>

I tried to make this request with an async/await, I tried methods mounted(), created(), beforeMount(), beforeCreate() but still it's the same. I'm new to Vue.js, and I am stuck here and don't know what to do.

Edit with the whole files to see the app structure: main.js

import router from './router'
import Vue from 'vue'
import App from './App.vue'
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
import axios from 'axios'
import Vuex from 'vuex'

// Install BootstrapVue
import 'leaflet/dist/leaflet.css';

Vue.use(BootstrapVue)
// Optionally install the BootstrapVue icon components plugin
Vue.use(IconsPlugin)
Vue.use(axios)
Vue.use(Vuex)
Vue.config.productionTip = false

const store = new Vuex.Store({
  state: {
    user : {}
  },
  mutations : {
    set_user (state,user) {
      state.user = user
    }
  }
})

export default store

/* eslint-disable */
router.beforeEach((to, from, next) => {
  if (from.path.indexOf("modificarCatalogo") == -1 && to.path.indexOf("modificarCatalogo") == -1) {
    localStorage.removeItem("catalogue");
  }
  if (localStorage.getItem("token") != null) {
    axios
      .get(`${process.env.VUE_APP_API_BASE_URL}/user/role`, {
        headers: {
          Authorization: "Token " + localStorage.getItem("token")
        }
      })
      .then(response => {
        store.commit('set_user', response.data);
        console.log("First then")
        console.log(store.state.user)
      }).catch(function (error) {
         // handle error case here
         console.log(error);

      }).then(function () {
         // always executed
         console.log("Second then")
         next();
      });
     }else{
        next();
     }
});
/* eslint-enable */

Vue.use(router)

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

It has now Vuex because I tried @ellisdod answer but

App.vue

<template>
  <div>
    <Header />
    <router-view />
    <Footer />
  </div>
</template>

And, in Header.vue, it is where I make the call to, in this case, Vuex $store, but it is the same. I need it to be done everywhere, so I tried to call the method in App.vue but still no results, it returns an empty object now with the solution of Vuex, but just empty, not with user data.

export default {
  name: "Header",
  data() {
    return {
      token: localStorage.getItem("token"),
      isOwner: "",
      estancoId: ""
    };
  },
  mounted() {
    console.log("Header log")
    if (this.$store.state.user.role == "Owner") {
      this.isOwner = true;
      this.estancoId = this.$store.state.user.estanco;
    }
  },

The rest of the components are irrelevant I think

2
  • 1
    Use Vuex for storing user info: vuex.vuejs.org Commented Mar 27, 2020 at 13:22
  • Thanks for your help! Does Vuex store the state of the user before the component is loaded? I mean, the problem here I think is that Axios makes the request after the component is loaded, so I think it would the be the same to use Vuex, wouldn't it? Commented Mar 27, 2020 at 13:31

2 Answers 2

0

If you use Vuex to store your user data you can prefill the user value with an empty object so it won't throw an error.

const store = new Vuex.Store({
  state: {
    user : {}
  },
  mutations : {
    set_user (state,user) {
      state.user = user
    }
  },
  actions : {
    loadUserFromLocal ({commit,state}) {
      if (localStorage.getItem("token") === null) return null
      return axios
        .get(`${process.env.VUE_APP_API_BASE_URL}/user/role`, {
        headers: {
          Authorization: "Token " + localStorage.getItem("token")
        }
      })
      .then(response => {
        commit('set_user', response.data);
        console.log("First then")
        console.log(state.user)
      }).catch(function (error) {
         // handle error case here
         console.log(error);
      })

    }
  }
})

new Vue({
  router,
  store,
  render: h => h(App),
}).$mount('#app')

Then in your mounted hook of your main App component add:

mounted () {
    this.$store.dispatch('loadUserFromLocal')
  }

Now in your router rather than making a server request before every route, you just check the store:

if (this.$store.state.user.role) {
  // handle logged in user
}
Sign up to request clarification or add additional context in comments.

5 Comments

Where should I put that "export default new Vuex.Store...." method? I tried it in main.js, the same where my router.beforeEach method is applied, but when I try this.$store.commit, I get an error: TypeError: Cannot read property '$store' of undefined
You just need to add it as one of the options when you instantiate vue - updated my answer
It's not giving the error anymore, however, it is returning the empty object on the first load of the page, because the method was not executed yet, so it is empty. I tried to run it in the App.vue in the created hook, but it is still the same.
I've updated my answer to move the server request to the store and dispatch it when the app mounts. If you are having issues with the store not loading, this arrangement may help: stackoverflow.com/a/45290290/6282604
I wish I could select your answer as a solution aswell... I mixed your solution of Vuex, with the one provided by spc, and I worked it out, thank you so much!!
-1

Hi,

Complete Answer based on Question Edit,Comments and Answers :

Problem

Vue-router's beforeEach method will only execute in components that are defined in the routes.
In your case,

  1. beforeEach will not be called in Header component as it is not part of routing. It is a standalone component.
    Therefore you cannot access $user inside it.

store.js

    import axios from 'axios'
    import Vuex from 'vuex'

    const store = new Vuex.Store({
    state: {
      user : {}
    },
    mutations : {
      set_user (state,user) {
        state.user = user
      }
    }
    actions : {
        loadUserFromLocal ({commit,state}) {
          if (localStorage.getItem("token") === null) return null
          return axios
            .get(`${process.env.VUE_APP_API_BASE_URL}/user/role`, {
            headers: {
              Authorization: "Token " + localStorage.getItem("token")
            }
          })
          .then(response => {
            commit('set_user', response.data);
            console.log("First then")
            console.log(state.user)
          }).catch(function (error) {
             // handle error case here
             console.log(error);
          })

        }
      }
    })
    export default store

@ellisdod - thanks.

Now you can use the user variable from store in your component and will be updated when the data is done fetched or will show initial values till that time

Therefore no need for fetching data in router.beforeEach

router.js

    // --> all imports
    import store from './store' // --> added
    //  --> all routes to be defined here... 

    router.beforeEach((to, from, next) => {
     // --> other logic for filtering routes
     // --> you can use 'store' variable here to get the user data and add 
    filtering 
    if (from.path.indexOf("modificarCatalogo") == -1 && 
    to.path.indexOf("modificarCatalogo") == -1) {
    localStorage.removeItem("catalogue");
     }
     next();

    });

As you know in vue-router, if next is called then navigation is said to be confirmed and the component will be rendered.
Also for more info on using store variable inside router.beforeEach method refer this question

main.js

    import router from './router'

    import store from './store' // --> added

    import Vue from 'vue'
    import App from './App.vue'
    import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
    import 'bootstrap/dist/css/bootstrap.css'
    import 'bootstrap-vue/dist/bootstrap-vue.css'
    import axios from 'axios'
    import Vuex from 'vuex'
    // Install BootstrapVue
    import 'leaflet/dist/leaflet.css';

    Vue.use(BootstrapVue)
    // Optionally install the BootstrapVue icon components plugin
    Vue.use(IconsPlugin)
    Vue.use(axios)
    Vue.use(Vuex)
    Vue.config.productionTip = false


    Vue.use(router)

    new Vue({
      router,
      store    // --> added
      render: h => h(App),
    }).$mount('#app')


App.vue

mounted () {
    this.$store.dispatch('loadUserFromLocal')
  }

@ellisdod - thanks.


Header.vue

    export default {
      name: "Header",
      data() {
       return {
        token: localStorage.getItem("token")
       };
      },
      computed: {
       isOwner() {
         return this.$store.state.user.role == "Owner"
       }
       estancoId () {
         return this.$store.state.user.estanco;
       }
      }
      mounted() {
       console.log("Header log")
      },
    }

9 Comments

That was a very nice approach I guess, however it didn't solve it, because axios is still being done after the component is loaded, even if we call next inside the then function. I tried to wait for it to be done by putting a v-if in my router-view call in App.vue, but still not working. Any guess on how to make it wait for axios to be done?
Can you check by logging to the console and confirm when the three functions are getting executed after axios.get? ie the two 'then' calls and the 'catch' call
Also if next() isn't called, the component to which you are navigating shouldn't load
Yes, I just did it to confirm. First, I added a console.log to every call, so the first call is the mounted method on my component, then the first "then" on beforeEach method, and the last one is the second then on beforeEach method. That is because "axios" method is running later than my component mounted method, and that's the problem.
Also.which component is loaded first in router as the '\home' route? and in which component are you accessing $user
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.