Skip to content
Advertisement

Dynamically changing props

On my app, I have multiple “upload” buttons and I want to display a spinner/loader for that specific button when a user clicks on it. After the upload is complete, I want to remove that spinner/loader.

I have the buttons nested within a component so on the file for the button, I’m receiving a prop from the parent and then storing that locally so the loader doesn’t show up for all upload buttons. But when the value changes in the parent, the child is not getting the correct value of the prop.

App.vue:

<template>
  <upload-button
    :uploadComplete="uploadCompleteBoolean"
    @startUpload="upload">
  </upload-button>
</template>

<script>
  data(){
    return {
      uploadCompleteBoolean: true
    }
  },

  methods: {
    upload(){
      this.uploadCompleteBoolean = false
      // do stuff to upload, then when finished,
      this.uploadCompleteBoolean = true
    }

</script>

Button.vue:

<template>
  <button
    @click="onClick">
  <button>
</template>


<script>
  props: {
    uploadComplete: {
      type: Boolean
    }

  data(){
    return {
      uploadingComplete: this.uploadComplete
    }
  },

  methods: {
    onClick(){
      this.uploadingComplete = false
      this.$emit('startUpload')
    }

</script>

Advertisement

Answer

Fixed event name and prop name then it should work.

  1. As Vue Guide: Custom EventName says, Vue recommend always use kebab-case for event names. so you should use this.$emit('start-upload'), then in the template, uses <upload-button @start-upload="upload"> </upload-button>

  2. As Vue Guide: Props says,

HTML attribute names are case-insensitive, so browsers will interpret any uppercase characters as lowercase. That means when you’re using in-DOM templates, camelCased prop names need to use their kebab-cased (hyphen-delimited) equivalents

so change :uploadComplete="uploadCompleteBoolean" to :upload-complete="uploadCompleteBoolean"

Edit: Just noticed you mentioned data property=uploadingComplete.

It is easy fix, add one watch for props=uploadComplete.

Below is one simple demo:

Vue.config.productionTip = false
Vue.component('upload-button', {
  template: `<div>  <button @click="onClick">Upload for Data: {{uploadingComplete}} Props: {{uploadComplete}}</button>
         </div>`,
  props: {
    uploadComplete: {
      type: Boolean
    }
  },
  data() {
    return {
      uploadingComplete: this.uploadComplete
    }
  },
  watch: { // watch prop=uploadComplete, if change, sync to data property=uploadingComplete
    uploadComplete: function (newVal) {
      this.uploadingComplete = newVal
    }
  },
  methods: {
    onClick() {
      this.uploadingComplete = false
      this.$emit('start-upload')
    }
  }
})

new Vue({
  el: '#app',
  data() {
    return {
      uploadCompleteBoolean: true
    }
  },

  methods: {
    upload() {
      this.uploadCompleteBoolean = false
      // do stuff to upload, then when finished,
      this.uploadCompleteBoolean = true
    },
    changeStatus() {
      this.uploadCompleteBoolean = !this.uploadCompleteBoolean
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
  <button @click="changeStatus()">Toggle Status {{uploadCompleteBoolean}}</button>
  <p>Status: {{uploadCompleteBoolean}}</p>
  <upload-button :upload-complete="uploadCompleteBoolean" @start-upload="upload">
  </upload-button>
</div>
User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement