I have an app for which I'm creating a custom component that will output a single row of a table. It contains a numeric field which is user-adjustable, and so within that custom component I'm using another custom component from the quasar framework (q-numeric
). I'm struggling to see how I can bind a variable at the top-level through 2 components - perhaps it can't be done directly, but I was hoping to avoid lots of extra code in the middle component. So far it looks like this from the top down:
In the App.vue template, I have lines like this:
<config-item v-model="numParticipants">Number of Participants</config-item>
ConfigItem.vue, looks like this:
<template>
<tr>
<td class="text-right"><slot></slot></td>
<td class="text-right">
<q-numeric
v-model="value"
:min="min"
:max="max"
:step="step"
@input="$emit('input', value)"
></q-numeric>
</td>
</tr>
</template>
<script>
export default {
props: {
label: String,
value: Number,
min: {
type: Number,
default: 1
},
max: {
type: Number,
default: 1000
},
step: {
type: Number,
default: 1
}
}
}
</script>
But of course that won't work, because I'm now binding the property value
to the q-numeric
, which will mutate it. Really, I wan't to bind the top-level variable numParticipants
to the q-numeric
- is it possible to implement some kind of 'pass through' within my config-item
component? Or does my component need to have it's own data element which it initialises from the passed-in properties, and updates in response to the q-numeric
? I know I can do that, but I was hoping for a cleaner solution...
Update: You can propagate v-model
-ability down a hierarchy by making a writable computed based on the prop (the prop must be named 'value'). The get
function obviously returns the prop value; the set
function does the $emit
.
The computed spec is entirely fixed, so I have extracted it as a constant.
const vModelComputed = {
get() {
return this.value;
},
set(newValue) {
this.$emit('input', newValue);
}
};
new Vue({
el: '#app',
data: {
numParticipants: 1
},
components: {
middleComponent: {
props: ['value'],
template: '<div>Value: {{value}} <q-numeric v-model="localValue"></q-numeric></div>',
computed: {
localValue: vModelComputed
},
components: {
qNumeric: {
props: ['value'],
template: '<div><input v-model="localValue" type="number"> inner value: {{value}}</div>',
computed: {
localValue: vModelComputed
}
}
}
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.min.js"></script>
<div id="app">
Participants: {{numParticipants}}
<middle-component v-model="numParticipants"></middle-component>
</div>
This is what I ended up with - seemed like the simplest approach. I was kind of hoping Vue would have a mechanism to hide this away when the 'middle' component effectively promises not to change the model, but just wants to hand it on to a child.
Basically, I created an intermediary data element ivalue
, and initialised it in the mounted()
event, and use the input()
event of the child to emit an input()
event back to the parent.
<template>
<tr>
<td class="text-right"><slot></slot></td>
<td class="text-right">
<q-numeric
v-model="ivalue"
:min="min"
:max="max"
:step="step"
@input="$emit('input', ivalue)"
></q-numeric>
</td>
</tr>
</template>
<script>
export default {
data: () => ({
ivalue: 0
}),
mounted () {
this.ivalue = this.value
},
props: {
label: String,
value: Number,
min: {
type: Number,
default: 1
},
max: {
type: Number,
default: 250000
},
step: {
type: Number,
default: 1
}
}
}
</script>
来源:https://stackoverflow.com/questions/43606355/in-vuejs-is-it-possible-to-bind-something-with-v-model-that-is-then-rebound-to-a