I have a simple button component for submitting forms and it should give a bit of feedback when clicked on it: show a spinner and disable the button while the form is being submitted and then also keep it disabled if there are any errors returned from the server until the errors are cleared (errors are cleared by changing the input of fields that have them).
Here's the template of the component:
<template>
<button type="submit" :disabled="disabled">
<loading-spinner v-if="submitting"></loading-spinner>
<slot>Submit</slot>
</button>
</template>
<script>
export default {
props: {
form: {
type: Object,
required: true
}
},
created() {
// Globally available event handler which listens for events
// emitted by Form object - it works fine
Event.$on('submitting', () => this.submitting = true);
Event.$on('submitted', () => this.submitting = false);
},
data() {
return {
submitting: false,
}
},
computed: {
// Here is the problem: this.form.errors.any() doesn't get updated in the
// computed prop even when errors are cleared. This same method, however,
// returns correct result when called directly via a test button
disabled() {
return this.submitting || this.form.errors.any()
},
// doesn't get updated either - apparently
// errors() {
// return this.form.errors.any()
// }
},
}
</script>
The button gets Form
object passed to so it can also have access to Errors
object which has some convenient methods to manage the errors. The Form
object that is passed as prop is reactive inside the submit-button
component and its fields get updated in Vue Devtools as I type into the form (on parent component), but the computed prop (disabled
or errors
) that relies upon it doesn't get updated.
When I submit the form with wrong data, I get errors, so form.errors.any()
returns true. However, once errors are returned the first time, the button turns disabled and it "freezes" like that (form.errors.any()
keeps returning true even when it isn't).
I know computed props are cached, but by definition they should update when their dependency gets updated (in this case the computed property disabled
should get updated because this.form.errors.any()
is updated). However, it seems that submit-button component "ignores" the update of this.form.errors.any()
in its computed property (it keeps returning true once errors are present and it never gets updated). The same thing happens if I make a same computed property on parent component.
Additional info
When I manually check its value (e.g. via a button that calls form.errors.any()
), it logs the correct value to the console every time, but it doesn't get updated in my computed property.
The any()
method on Errors
object - nothing fancy here
any() {
return Object.keys(this.errors).length > 0;
}
The Form and Errors object both work fine: their methods return normal values when called directly (via a button click, for example) from any component and they dispatch events as expected, they just don't react on a computed prop.
Workaround
The solution for me was to use a method instead of computed property, but it's a bit too verbose and I'd mostly like to know why can't I use more elegant computed property in this case?
<template>
<button type="submit" :disabled="submitting || anyErrors()">
<loading-spinner v-if="submitting"></loading-spinner>
<slot>Submit</slot>
</button>
</template>
<script>
export default {
...
methods: {
anyErrors() {
return this.form.errors.any()
}
}
}
</script>
this.errors
is an empty object on instatiation (constructor) and it can be accessed/populated/cleared through methods (get/register/clear and already mentioned any). – tal3nce