-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamic v-model directive #1056
Comments
Can you give an example of what it would be helpful for? |
Let's, say, imagine we're building a clone of phpmyadmin, which receives data from DESCRIBE TABLE statement and builds row editor form from that data. Binding expressions will be inherently dynamic in this case, as we'll only know field names after running SQL DESCRIBE TABLE. |
+1 , i am looking for this too |
+1, hope to see this |
I still don't fully understand what this enables that the current syntax cannot achieve. Maybe some code samples? |
Some pseudo-code related to phpmyadmin clone described above:
|
you can already do that with |
we can ? wow ! |
evan can you put up a js fiddle showing a todo-ish example |
@yyx990803, that's great, but it was just an example showing just one example of dynamic usage. The logic might be more complex in some kind of a business application. |
Just to be clear I'm against the idea of allowing interpolations inside directive expressions. Right now mustaches means the evaluated result is expected to be a string and used as a string (to be inserted into the DOM, or do a ID lookup). Evaluating mustaches into expressions which then can be evaluated makes it two layers of abstraction and can end up making your templates very confusing. |
i think it would be very valuable to add the ability to interpolate the string before evaluating the expression the maybe add a modifier to the binding so that is it more clear than mustaches Example
or maybe a getter/setter function that can be passed. disclaimer: i have not read the 2.0 spec so you may have addressed this there. |
@bhoriuchi why no computed property? computed: {
varPath: function() {
return this.myData.path.to['my'].obj;
},
}, <component-name :myparam="varPath"></component-name> And for |
@simplesmiler i have not tried computed properties in a two-way binding, ill give it a shot. thanks for the tip. Update @simplesmiler - so the issue i am running into with using a computed property is that i have no way to pass arguments to the computed property. some background on my use case. i am creating a form builder that uses a json object to build the forms. the config object is more or less a 2 dimensional array of objects (rows/forms). each form config object has a model field that has the path string to the field that should be set. in order to use a computed property for this i would need to be able to determine from the component using the component binding what row/form index in order to look up the model path from the config object currently i have this working using a pre-initialized 2 dimensional array called i need 2 levels of nesting because i am using this form builder component on another component that needs to set an object that looks something like
where my path string would look like
Update 2 i changed from using a multi-dimensional array to a plain object with key names
and used a deep watch to keep the data in sync with the parent. I still think an interoplated bind would be beneficial. |
+1 |
For me, |
@victorwpbastos this does not work for setting deeply nested objects as it will just use the field.name as the key for example if you have the following data and field string
and you use
and enter the value of 2 on the form, the data would end up looking like this
the reason interpolated bind is useful is where you are building dynamic nested inputs where you cant "hard code" the parent path (e.g 'animal.dog') into the directive |
I revisited this found a more simple solution. You can create a custom object and add getters/setters to it on created using the model path string. Here is a simple example input-list <template lang="jade">
div
div(v-for="form in config.forms")
input(v-model="formData[form.model]")
</template>
<script type="text/babel">
import Vue from 'vue'
import _ from 'lodash'
export default {
props: ['value', 'config'],
computed: {},
methods: {
vueSet (obj, path, val) {
let value = obj
let fields = _.isArray(path) ? path : _.toPath(path)
for (let f in fields) {
let idx = Number(f)
let p = fields[idx]
if (idx === fields.length - 1) Vue.set(value, p, val)
else if (!value[p]) Vue.set(value, p, _.isNumber(p) ? [] : {})
value = value[p]
}
}
},
data () {
return {
formData: {}
}
},
created () {
_.forEach(this.config.forms, (form) => {
Object.defineProperty(this.formData, form.model, {
get: () => _.get(this.value, form.model),
set: (v) => this.vueSet(this.value, form.model, v)
})
})
}
}
</script> in use <template lang="jade">
div
input-list(v-model="formData", :config='formConfig')
</template>
<script type="text/babel">
import InputList from './InputList'
export default {
components: {
InputList
},
data () {
return {
formData: {
name: 'Jon',
loc: {
id: 1
}
},
formConfig: {
forms: [
{ type: 'input', model: 'loc.id' },
{ type: 'input', model: 'loc["name"]' }
]
}
}
}
}
</script> |
If using this way, any way we can set the watcher for each of the reactive data created dynamically? |
@luqmanrom I am not familiar with the inner workings of the vue watcher but I believe anything created with vue.set can be watched so you could add some code to watch dynamic props and emit evens on changes or you can seep watch the target object. Someone else might have a better suggestion |
I wrote a toolkit for this. also allows you to mutate vuex using v-model |
This should do the trick: Directive Vue.directive('deep-model', {
bind(el, binding, vnode) {
el.addEventListener('input', e => {
new Function('obj', 'v', `obj.${binding.value} = v`)(vnode.context.$data, e.target.value);
});
},
unbind(el) {
el.removeEventListener('input');
},
inserted(el, binding, vnode) {
el.value = new Function('obj', `return obj.${binding.value}`)(vnode.context.$data);
},
update(el, binding, vnode) {
el.value = new Function('obj', `return obj.${binding.value}`)(vnode.context.$data);
}
}); Usage (Component) const component = Vue.extend({
template: `<input v-deep-model="'one.two.three'">`,
data() {
return {
one: { two: { three: 'foo' } }
};
}
}); Here is the Gist Reference. |
Hi any body here. I am using VUE.js with Laravel. I have Dynamic Custom Form fields coming from the database. I followed @yyx990803 . v-model="form['name']". The field works. But the problem is i can not get the field values in laravel Controller. Anybody here. I am using @tylerOtwell Form.js Class. |
This is not a help forum. We have one dedicated for answering questions at https://forum.vuejs.org |
Currently I have solved the problem by exploiting the fact that computed properties are function calls. Scriptcomputed: {
isActive() {
return this.presets.map(
preset =>
preset.range[0] === this.startDate && preset.range[1] === this.endDate
);
}
} Template<li v-model="isActive[index]" v-for="(preset, index) in presets">
...
</li> But it really seems like a hack to me. Not sure. Please suggest. |
Does anybody know if this also works in combination with Vuex as explained here? https://vuex.vuejs.org/guide/forms.html I want to have an input field which is a little bit dynamic.
How can I access "scope" of the dom element inside the following code?
|
@vielhuber try to use <input ref="myInput" v-model="dataHandler" :scope="foo" type="checkbox" /> this.$refs.myInput.getAttribute('scope') // => 'foo' |
Hi, I have a Vue question related to this topic - "dynamic v-model directive": When I'm implementing a Vue component, how can I dynamically control the v-model modifier - <el-input v-model[field.lazy ? '.lazy' : '']="model[field.key]"> |
This works for me.
|
@fritx To "dynamically" change the modifier, I used the v-if director like this.
This can get cumbersome though if you want large variety of multiple combinations of modifiers. I guess one option could be to create a reusable component that contains all the if statements and pass it the input component you want to render and the array of modifiers that determines which input with the desired modifiers is rendered. Using the if statement like above though was good enough for me. |
I could not find the way for dynamically accessing computed property in v-model directive. My code is something like this:
I need the way to access computed property with string, which i couldn't find. The closest thing I have found is "_computedWatchers[someDynamicString].value" but that does not work with setters and getters, maybe it would work if it was just a computed value. |
v-model="dialogTemp.tmBasFuelEntities[dialogTemp.tmBasFuelEntities.findIndex(t=>t.paramCode==item.paramCode)].paramValue" This is my dialogTemp: dialogTemp: {
tmBasFuelEntities: [
{
paramCode: '',
paramValue: ''
},
{
paramCode: '',
paramValue: ''
},
{
paramCode: '',
paramValue: ''
},
]
} |
It's cool but I had to pass lots of props to the very one which is so verbose, any idea? @danhanson <template v-else-if="itemCom">
<component v-if="getFieldType(field) === 'number'"
:is="itemCom"
:model="model"
:field="field"
:schema="schema"
v-model.number="model[field.key]"
v-loading="field.loading"
v-bind="getFieldAttrs(field)"
v-on="field.listen"
@form-emit="handleFormEmit"
></component>
<component v-else
:is="itemCom"
:model="model"
:field="field"
:schema="schema"
v-model="model[field.key]"
v-loading="field.loading"
v-bind="getFieldAttrs(field)"
v-on="field.listen"
@form-emit="handleFormEmit"
></component> |
@fritx You could change </template>
<component v-if="getFieldType(field) === 'number'"
:is="itemCom"
:model="model"
:field="field"
:schema="schema"
:value="parseField(field, model[field.key])"
@input="model[field.key] = parseField(field, $event.target.value)"
v-loading="field.loading"
v-bind="getFieldAttrs(field)"
v-on="field.listen"
@form-emit="handleFormEmit"
></component>
<template>
<script>
export default {
...
methods: {
parseField (field, val) {
if (this.getFieldType(field) === 'number') {
return Number(val);
}
return val;
}
}
};
</script> |
@danhanson looks great, man |
@danhanson I'm afraid it should be: :value="getFieldValue(field, model[field.key])"
@input="model[field.key] = getFieldValue(field, $event)"
@change="model[field.key] = getFieldValue(field, $event)" I'm not sure, I'll try. Thanks! |
I found a solution here: https://forum.vuejs.org/t/accessing-computed-properties-from-template-dynamically/4798/9
|
Something like this <el-input
v-if="!nestedField.widget"
v-model="form[nestedField.id]"
placeholder=""
v-bind="nestedField.rest"
> [
{
label: '收房价格',
id: 'housePrice',
type: Number,
widget: 'div',
fieldSet: [
{
label: '',
id: 'housePrice',
type: Number,
defaultValue: 0,
rest: {
style: 'width:5em;'
},
},
{
label: '',
id: 'priceUnit',
type: String,
widget: 'select',
defaultValue: '元/月',
options: [
{ label: '元/月', value: '元/月' },
{ label: '元/年', value: '元/年' },
{ label: ' 元/天·m2', value: '元/天·m2' },
],
rest: {
style: 'width:6em;'
},
},
],
},
] When field type is |
I teardown v-model to fit it. <el-input
v-if="!nestedField.widget"
:value="form[nestedField.id]"
@input="v => { form[nestedField.id] = isNumber(nestedField.type) ? Number(v) : v }"
placeholder=""
v-bind="nestedField.rest"
>
<template v-if="nestedField.suffixText" slot="append">{{nestedField.suffixText}}</template>
</el-input> |
I Have clone HMTL using (some part for form-input) which i am inserting using jquery. (don't say why i am using jquery). now my element is being inserted by jquery. so is it possible to bind v-model.
So how i can bind v-model on area 2 and 3. |
Works for me too, but the "_self" variable is reserved for Vue's internal properties (see #2098). In other words, this implementation can breaking in the future. I prefer this way:
For more details see: https://stackoverflow.com/questions/52104176/use-of-self-attribute-from-vue-vm-is-reliable |
|
I know this may be late. First start with component
|
Currently, v-model directive does not support mustache-type bindings for binding expressions, but this feature would be extremely helpful for creating form builder-like implemenetations.
The text was updated successfully, but these errors were encountered: