I am working on the communication of two components, where one is a button, which can have states such as initial, loading, and concluded (successfully or not), and for each state of the button, I might have a different text displayed, different icon (loading: spinning icon, conlcuded with sucess: check mark, concluded with error: an x), and I also have a form, which will be using the button component. My doubt is how to change states on the button based on current state of the form submission. Check the code below.
My button component:
<template>
<button
class="ui-button"
@click="clicked"
:data-status-type="status_type"
:disabled="is_disabled"
:type="type"
>
<i :class="icon" v-if="is_disabled || concluded"></i>
{{ title }}
</button>
</template>
<script>
export default {
props: {
title: {
type: String,
},
type: {
default: "button",
type: String,
},
},
data() {
return {
concluded: false,
icon: "fa fa-spin ",
is_disabled: false,
status_type: "success",
};
},
methods: {
clicked() {
if (!this.is_disabled) {
this.$emit(
"clicked",
() => {
this.is_disabled = true;
this.icon = "fa fa-spin fas fa-spinner";
},
(succeeded) => {
this.is_disabled = false;
this.concluded = true;
this.icon = succeeded ? "fas fa-check" : "fas fa-xmark";
this.status_type = succeeded ? "success" : "error";
setTimeout(() => {
this.concluded = false;
this.icon = "";
this.status_type = "";
}, 1500);
}
);
}
},
},
};
</script>
And my form component:
<template>
<div>
<ThePages :parents="accompaniments">
<!-- ... some reactive stuff -->
<template #extra_button>
<TheButton @clicked="sendItemToCart" :title="button_text" :disabled="button_disabled" />
</template>
</ThePages>
</div>
</template>
<script>
import axios from 'axios'
import FormatHelper from '../helpers/FormatHelper'
import SwalHelper from '../helpers/SwalHelper'
import TheButton from './TheButton.vue'
import ThePages from './ThePages.vue'
import TheQuantityPicker from './TheQuantityPicker.vue'
export default {
props: ['product'],
components: {
TheButton,
ThePages,
TheQuantityPicker,
},
data() {
return {
accompaniments: this.product.accompaniment_categories,
button_text: '',
button_disabled: false,
format_helper: FormatHelper.toBRCurrency,
observation: '',
quantity: 1,
success: false,
}
},
created() {
this.addQuantityPropToAccompaniments()
this.availability()
},
methods: {
// ... some other methods
async sendItemToCart(startLoading, concludedSuccessfully) {
startLoading() // This will change the button state
this.button_text = 'Adicionando...'
await axios
.post(route('cart.add'), {
accompaniments: this.buildAccompanimentsArray(),
id: this.product.id,
quantity: this.quantity,
observation: this.observation,
})
.then(() => {
concludedSuccessfully(true) // This will change the button state
this.button_text = 'Adicionado'
SwalHelper.productAddedSuccessfully()
})
.catch((error) => {
concludedSuccessfully(false) // This will change the button state
if (
error?.response?.data?.message ==
'Este produto atingiu a quantidade máxima para este pedido.'
) {
SwalHelper.genericError(error?.response?.data?.message)
} else {
SwalHelper.genericError()
}
this.button_text = 'Adicionar ao carrinho'
})
},
},
}
</script>
In the code above, you can see how I am making my button change according to the state of the form: my button is emitting out two function when clicked (startLoading, concludedSuccessfully) and then I am using these two functions inside sendItemToCart.
This seem like coupling the two components a bit too much, since I have to keep pasing the functions as parameters to the parent's methods. Also, I have another ideia on how I could do it, which would be by giving each button a ref and then calling its methods on the parent using the ref. This ideia sounds a bit like the "Composition rather than inheritance" from OOP, where I'd just ask the object/component to do something, but, in this case, without the functions as parameters.
Well, both cases above seem better than keep creating variables on the data for each button I might have, but they seem like they could get improved. So that's what I am looking for: how to better decouple my components?