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.
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>
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>