vuejs2axiosvue-routerevent-bus

Using EventBus to pass userID to call API but will not bind response to input field


I used an EventBus to pass the UserId to another component (UpdateStaff). Then, I want to call an API at url/api/staffs/{UserId}, to get the data of that particular user and bind the data to the input fields. I'm able to get the response payload but unable to bind to the input fields.

UpdateStaff.vue:

An example of an input field in the template:

<input class="input" type="text" placeholder="Username" v-model="data.username">

Data properties & created method:

data () {
  return {
    data: {
      name: '',
      username: ''
    }
  }
},
created () {
  EventBus.$on('getUserId', (userId) => {
    axios.get(staffUrl + '/' + userId)
      .then((response) => {
        let self = this
        self.data.username = response.data.username
        self.data.name = response.data.name

        console.log(self.data.username)
        // It prints out exactly what i want
        // BUT it doesn't show the data in the input field....?
      })
      .catch((error) => {
        console.log(error)
      })
  })
}

Staff.vue:

Has a vuetable component in it to display all Staffs. After clicking the Update Button, I redirect to the UpdateStaff page via calling an onActions method.

methods: {
  onActions (action, data) {
    router.push({ path: '/user/UpdateStaff' })
    EventBus.$emit('getUserId', action.data.userId)
  }
}

Previously, I was able to display data in input fields with just axios alone. However, after adding the EventBus with axios in it, I was unable to display data in input fields anymore.

How should I go about fixing this issue?


Solution

  • The reason you're not seeing the data update correctly is that this is not the Vue instance when you reference it in the .then callback.

    Put the let self = this statement at the very beginning of your created hook to be sure you are storing the correct reference to this:

    created () {
      let self = this
      // after this point, `self` will reference the Vue instance even in callbacks
    
      EventBus.$on('getUserId', (userId) => {
        axios.get(staffUrl + '/' + userId)
          .then((response) => {
            // setting `self` to `this` here doesn't make sense because `this` 
            // is not refering to the Vue instance in this callback
    
            self.data.username = response.data.username
            self.data.name = response.data.name
          })
          .catch((error) => {
            console.log(error)
          })
      })
    }
    

    The reason the getUserId event isn't being handled when you push to that route is that the $emit event is being fired before the UpdateStaff component is created (meaning the $on handler hasn't been set yet).

    To wait for the component to have been created, wrap the $emit in a this.$nextTick callback:

    methods: {
      onActions (action, data) {
        router.push({ path: '/user/UpdateStaff' })
        this.$nextTick(() => {
          EventBus.$emit('getUserId', action.data.userId)
        })
      }
    }
    

    From the documentation on the usage of Vue.nextTick:

    Defer the callback to be executed after the next DOM update cycle. Use it immediately after you’ve changed some data to wait for the DOM update.