Strange behaviour with prop inheritance (modelValue)
Version
3.0.0-rc.9
Reproduction link
https://jsbin.com/wukoruyuru/edit?html,js,output
Steps to reproduce
<div id="app">
<component-a v-model="myData"></component-a>
<span>Data: {{ myData }}</span>
</div>
const app = Vue.createApp({
data() {
return {
myData: 'initial'
}
}
});
app.component('component-a', {
props: {
modelValue: String
},
template: `
<component-b
:modelValue="modelValue"
@update:modelValue="$emit('update:modelValue', 'from A')"
/>`
})
app.component('component-b', {
props: {
modelValue: String
},
template: `
<button
@click="$emit('update:modelValue', 'from B')"
>Click B</button>`
})
app.mount('#app')
What is expected?
modelValue props (maybe props with the same name) should be isolated in each component.
What is actually happening?
There seems to be a different behaviour in Vue 3 compared to Vue 2. When passing data to a v-model of a component (Component A), then passing it to another component (Component B), the data does not get modified/emitted in the first one (Component A).
Please see the example. I expect the myData to be ‘from A’ (because it should be overwritten in Component A), but instead it results in ‘from B’. Even if I remove the $emit-Line in Component A I still get the same result. This is different from Vue2, is this the intended behaviour?
When I replace modelValue in ComponentB to let’s say modelValue1 and set its other references stuff then the sample is working like expected.
So when using a prop “modelValue” in a component and also “modelValue” in one of its sub components then the “bug” appears…
If I append an empty <span/>
to component-a
template, making it a Fragment, it works as expected. So it's possible that the problem is an over-optimization.
I believe this is because onUpdate:modelValue
is in the $attrs
so it's automatically being added to the root element of the child.
Explicitly adding emits: ['update:modelValue'],
will remove the event from the $attrs
, fixing the problem. It's not entirely intuitive though as the original code would work fine in most cases, only failing when the child happens to be the root element.
I think it's important to distinguish model update events from any other events exactly for this to work. Model update event listeners should have a special treatment which they do have right now. But in a case of update:
we can't do that because update:
doesn't tell that this event is a model update event. You could emit for example a update:foo
event that would be handled like a model update event, which it is not.
I've created an RFC to fix that.