Should v-model work on components using both v-bind="$attrs" and v-on="$listeners"?
See original GitHub issueWhat problem does this feature solve?
Following this discussion, given a CustomInput
component with this template:
<div>
<input v-bind="$attrs" v-on="$listeners">
<div>
The following currently does not work:
<CustomInput v-model="myValue" />
The problem is that v-model
on a component expects a value on input
events, but a DOM Event
is passed to the listener instead.
What does the proposed API look like?
Option 1: Make v-model
smarter
Since I think it’s unlikely that anyone will want to use v-model
with a DOM Event
, perhaps when v-model
is used on components, it could check if the argument passed to the listener is instanceof window.Event
. Then if it is, use event.target.value
instead.
Option 2: Make v-on
smarter
Maybe a non-enumerable property could be added to $listeners
(e.g. __$listeners__: true
, to help v-on
detect uses of v-on="$listeners"
. Then in cases where the $listeners
object is passed to v-on
and it’s used on an element, listeners relevant to v-model
could be wrapped:
function (event) {
listener(event.target.value)
}
But now we’re throwing away data. If someone wants to access a keyCode
- e.g. with @input
instead of v-model
- they’re out of luck. However, if modifiers were also supported for v-on
’s object syntax, we could fix this by making .native
disable the automatic wrapping behavior.
Thoughts? Are there strategies or implications I’m not considering? I’m definitely open to alternatives.
Issue Analytics
- State:
- Created 6 years ago
- Reactions:16
- Comments:19 (12 by maintainers)
All we want is to be able to create “fully transparent wrapper” components and add some functionality/customization on top… to have some semblance of inheritance in Vue. Why all these edge cases and special syntax just for that? Why is it not the default behavior already?
So, I finally gave this proposal a proper investigation, and it ends up being more complicated than it seems. The problem lies within consistency between different input types. The current implementation in #6327 assumes a text input and ignores non-input events. While this simplifies wrapping plain text input fields, it makes the whole thing inconsistent and feels like a special-case convenience hack.
I managed to make it work with
<input type="checkbox">
, which requires the component to specify themodel
option, and then injecting the event type and prop type back into the componentv-model
handler. It’s already messier than I hoped - but even if this works, it becomes prohibitively complex to mimic the full behavior of nativev-model
(e.g. checkboxes with array binding, radio, select…)At this point I feel the implementation cost and the inconsistency outweighs the simplification this change would bring.
Taking a step back, the whole idea of component
v-model
is opening up finer-grained control over the two-way binding of form inputs, not simplifying it. Its job is expanding the sugar consistently into a pair of input and output, and letting you fill in the details. Mixing that up with$attrs
and$listeners
feels like magic where it doesn’t belong.