The Context
I’m building a big form with lots of inputs in Vue.js 2.6.10 using class-based components in Typescript 3.5.3. (In the code snippets below I’ve converted it to JS since I’m positive that the TS is not the problem.) In order to not overwhelm the user, the form is broken down in multiple components; only one at a time is shown to the user.
Parent component
<template>
<form>
<Basic :Container.sync="Container"/>
<!-- More custom components -->
<output>{{Container.name}}</output>
</form>
</template>
The Container
object is a wrapper for all the data to be collected. You see I’m passing the whole object instead of only the properties that are asked for to the child components. This may seem opinionated, but I have my reasons.
The Form component boils down to this:
// Imports left out for brevity
@Component({ components: { Basic, }, })
export default class Form extends Vue {
private Container
async created() {
this.Container = {
name: null
}
setInterval(()=>{console.log(this.Container.name)}, 5000)
// Logs whatever will be entered in the input in the Basic component
}
}
Child component
The referenced component Basic
is a plain input element:
<template>
<input v-model="Container.name" @input="emitUpdate">
</template>
with an equally trivial component definition:
// Imports left out for brevity
@Component({})
export default class Basic extends Vue {
@Prop() Container
emitUpdate() {
console.log(this.Container.name) // Logs whatever was entered in the input
this.$emit('update:container', this.Container)
}
}
The Problem
What I’m expecting to see is the container to update, and the <output>
from the first HTML snippet to display the current name. However, it doesn’t, even though the variable is changed, as is proven by the two console logs which both register the input value correctly.
It seems like Vue’s reactivity cannot register the change to the Container
object and thus won’t re-render any of the HTML. How can I fix this?
Attempted solutions
I’ve tried listening explicitly for the update events using <Basic … :Container.sync="Container" @Container="Container = $event" />
, but to no avail.
Thinking that Vue might just not react to changes in object properties (after all, watching objects requires the deep
option to be set, too), I’ve also tried to pass the Container’s properties individually, leading to the following changes:
In the form’s html the object is used as the argument to v-bind (I’ve tried with and without @update:name
):
<Basic … v-bind.sync="Container" @update:name="name=$event"/>
And in the child component I’ve changed to a getter and a setter in order to get around the “Thou shall not change Props” warning:
<input v-model="Name" @input="emitUpdate">`
@Prop() name
get Name() {return this.name }
set Name(newName) {this.$emit('update:name', newName)}
TS
, should I? – Andrew Vasilchuk