I'm fairly new to coding in Vue.js (3) and the project I have stepped into uses CoreUI for Vue as its main framework for forms and displays. I'm not sure if this is a bug in the CoreUI CMultiSelect itself, but when a user clicks 'Select all options' in one of the form dropdowns, the form automatically submits before the user can fill out the rest/submit it for themselves. I'm especially confused because even when I disconnect the component from the custom @change
(I just deleted the line) defined to save their selection, it still demonstrates the problem. Any help would be appreciated!
Side note: I am also unfamiliar how to disable the select-all option, though it mentions this is possible in the CoreUI Vue MultiSelect Documentation. I set select-all="false"
within the component to no avail. If this is what I have to do for the time being until I figure this out, I would appreciate guidance on how to do so as well.
Here are the component and selection options:
<CMultiSelect
placeholder="Application Types"
:options="battery_application_choices"
@change="batteryappChanged($event)"
>
<!-- inside data() -->
battery_application_choices: [
{ text: 'Demand Reduction', value: 'Demand Reduction' },
{ text: 'Energy Arbitrage', value: 'Energy Arbitrage' },
{ text: 'Frequency Response', value: 'Frequency Response' },
{ text: 'Microgrid Component', value: 'Microgrid Component' },
{ text: 'Other', value: 'Other' },
{ text: 'Solar / Storage', value: 'Solar / Storage' },
{ text: 'UPS', value: 'UPS' },
],
I found how to implement my temporary fix, where I needed to type :select-all="false"
with a colon to bind it instead of select-all="false"
. For newbies like me, :
is short for v-bind
in Vue, which binds data to components so that when the data changes, the state of the app changes to reflect it.
In order to describe the robust solution to this problem, I will be outlining what I learned about JS events and how Vue handles watching them and their propagation. I will also be covering a bit of how Vue uses what you write to generate HTML code, specifically how the CoreUI multiselect component generates HTML elements like <select>
and <options>
.
What was causing the error was the fact that the <CMultiSelect>
was nested in a parent <CForm>
. This in itself is not bad practice, and is even practical given that some forms should have multiple selection fields alongside their other form fields. However, a problem arose when a user clicked the 'Select all options' button. This is because putting a <CMultiSelect>
into your code instructs Vue to compose a collection of elements at runtime that are abstracted away from you, the developer. Vue generates a <select>
block that contains an <option>
element for each option that you bind to the <CMultiSelect>
. It also generates a 'Select all options' <button>
that for some reason can produce a SubmitEvent
. When this event is produced, it propagates upward since it was not stopped with event.preventDefault()
or event.stopPropagation()
. It gets caught by the submission watcher of the parent <CForm>
and triggered the custom form submission in my project. This behavior was also caused by the small x
buttons to delete tags from the multiselect view, but I did not look into the generated code with console.log()
s enough to figure out why.
I was able to fix this problem by checking the submitter
property of event
.
Side note: I would recommend doing a quick
console.log(event)
in any event handler methods that you are having trouble with, since events are much more complex than I thought and you will be able to see properties that can be of use to you.
event.submitter
is a property of submit events, and returns the HTML element that generated that particular submission. In my case, it returned the 'submit(?)' button of the <CMultiSelect>
which was of the form <button ...>Select all options</button>
. I then accessed just the contents with event.submitter.innerHTML
(which produced just Select all options
) and if it matched this content, do nothing. This could also be used for a form with multiple submission buttons that trigger different types of submission (perhaps alternate processing of user data), because it allows you to tell where the event is coming from.
<form @submit.prevent="customHandler($event)">
<button type='submit'>Save</button>
<button type='submit'>Change</button>
</form>
customHandler(event) {
if (event.submitter.innerHTML == 'Save') {
// Save user data
} else if (event.submitter.innerHTML == 'Change') {
// Change user data in different way than 'Save'
} else {
// Do nothing
// The CMultiSelect's 'Select all options' event will fall here
}
}
Since this issue took about 3 cumulative workdays out of my schedule I figured the post I made about it should carry the same weight. I know this is overkill but if even one other developer can avoid the same problem then it'll have been more than worth it. I would say I learned that JavaScript is so much more complicated than I thought imaginable, yet frustratingly stupid at the same time, but that would mean that I didn't know that before...