1π
A properly encapsulated child component would be decoupled from the parent data structure. It would take type
and value
as separate props, plus an opaque id
to tell the parent which value the component is emitting about.
By making a settable computed based on the value
parameter, the component can use v-model
on its form elements. The set
function emits an input
event with the id
and the newValue
, and the parent takes it from there.
Update: I decided I didnβt like the id
going to the component, so I handled that in the input handler: @input="updateField(index, $event)
.
new Vue({
el: '#app',
data: {
schema: [{
type: 'text',
name: 'one',
value: "1"
},
{
type: 'textarea',
name: 'two',
value: "stuff in the textarea"
}
]
},
methods: {
updateField(index, newValue) {
this.schema[index].value = newValue;
}
},
components: {
formInput: {
props: ['type', 'value'],
computed: {
proxyValue: {
get() { return this.value; },
set(newValue) {
this.$emit('input', newValue);
}
}
}
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
<div v-for="field in schema">
{{field.name}} = {{field.value}}
</div>
<form-input inline-template v-for="field, index in schema" :type="field.type" :key="index" :value="field.value" @input="updateField(index, $event)">
<div>
<input v-if="type === 'text'" v-model="proxyValue">
<textarea v-if="type === 'textarea'" v-model="proxyValue"></textarea>
</div>
</form-input>
</div>
1π
For what you are doing here, there is no need to separate the form into a component. Just make it part of the parent and use v-model
.
new Vue({
el: '#app',
data: {
schema: [{
type: 'text',
name: 'one',
value: "1"
},
{
type: 'textarea',
name: 'two',
value: "stuff in the textarea"
}
]
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
<div v-for="field in schema">
{{field.name}} = {{field.value}}
</div>
<div v-for="field in schema">
<input v-if="field.type === 'text'" v-model="field.value">
<textarea v-if="field.type === 'textarea'" v-model="field.value"></textarea>
</div>
</div>
If you want the component for reusability and you donβt care about insulating the parent from changes (best practice is not to have anything outside a component change its data), you can just wrap the same thing in a component:
new Vue({
el: '#app',
data: {
schema: [{
type: 'text',
name: 'one',
value: "1"
},
{
type: 'textarea',
name: 'two',
value: "stuff in the textarea"
}
]
},
components: {
formChild: {
props: ['value']
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
<div v-for="field in schema">
{{field.name}} = {{field.value}}
</div>
<form-child inline-template v-model="schema">
<div>
<div v-for="field in value">
<input v-if="field.type === 'text'" v-model="field.value">
<textarea v-if="field.type === 'textarea'" v-model="field.value"></textarea>
</div>
</div>
</form-child>
</div>