Vue: 动态 v-model 指令

创建于 2015-07-16  ·  42评论  ·  资料来源: vuejs/vue

目前,v-model 指令不支持绑定表达式的 mustache 类型绑定,但此功能对于创建类似表单构建器的实现非常有帮助。

最有用的评论

你已经可以用v-model="form[field.name]"做到这一点。

所有42条评论

你能举一个例子说明它有什么帮助吗?

假设我们正在构建一个 phpmyadmin 的克隆,它从 DESCRIBE TABLE 语句接收数据并从该数据构建行编辑器表单。 在这种情况下,绑定表达式本质上是动态的,因为我们只有在运行 SQL DESCRIBE TABLE 后才能知道字段名称。

+1,我也在找这个

+1,希望看到这个

我仍然不完全理解当前语法无法实现的功能。 也许一些代码示例?

上面描述的一些与 phpmyadmin clone 相关的伪代码:

    <script>
    modue.exports = {
        data: function(){
            //returns table structure pulled from the backend somehow
            return {fields: [
                {name: "id", type: "integer"},
                {name: "name", type: "varchar"},
                {name: "gender", type: "varchar"}
            ], 
            // this was initialised based on the structure above, does not matter how.
            form: {id: null, name: null, gender: null}); 
        },
       methods: {
          getBindingExpr: function(field){ /* blah-blah */ }
       }

    }
    </script>
    <template>
       <div v-repeat="field: fields">
          <!-- Here we need to display an editor bound to the field -->
           <input type="text" v-model="form.{{field.name}}">
        <!-- Or, we can call a function that calculates the binding expression --
          <input type="text" v-model="{{getBindingExpr(field)}}">
      </div>
    </template>

你已经可以用v-model="form[field.name]"做到这一点。

我们可以 ? 哇 !

evan 你能放一个 js fiddle 来展示一个 todo-ish 的例子吗

@yyx990803 ,太好了,但这只是一个示例,仅显示了一个动态使用示例。 在某种业务应用程序中,逻辑可能更复杂。

为了清楚起见,我反对在指令表达式中允许插值的想法。 现在 mustaches 意味着评估结果应该是一个字符串并用作字符串(插入到 DOM 中,或进行 ID 查找)。 将 mustache 评估为表达式,然后可以对其进行评估,这使其成为两层抽象,最终会使您的模板变得非常混乱。

我认为在评估表达式之前添加插入字符串的能力会非常有价值

data[pathString]方法适用于具有 1 个嵌套级别的对象,但对于 2 个或更多我还没有找到动态绑定的方法。

也许在绑定中添加一个修饰符,这样它比胡子更清晰

例子

let myData = {}
let varPath = 'myData.path.to["my"].obj'
let modelPath = 'myData.path.to["my"].model'
<component-name :myparam.interpolate='varPath'></component-name>
<input v-model.interpolate='modelPath'>

或者可能是一个可以传递的 getter/setter 函数。

免责声明:我没有阅读 2.0 规范,所以你可能已经在那里解决了这个问题。

@bhoriuchi为什么没有计算属性?

computed: {
  varPath: function() {
    return this.myData.path.to['my'].obj;
  },
},
<component-name :myparam="varPath"></component-name>

对于v-model您可以将计算属性与 setter 一起使用。

@simplesmiler我还没有在双向绑定中尝试过计算属性,我

更新

@simplesmiler - 所以我在使用计算属性时this甚至value中的get(value)都指向组件。

我的用例的一些背景。

我正在创建一个使用 json 对象来构建表单的表单构建器。 配置对象或多或少是一个二维对象数组(行/表单)。 每个表单配置对象都有一个模型字段,其中包含应该设置的字段的路径字符串。 为了为此使用计算属性,我需要能够使用组件绑定从组件中确定什么行/表单索引,以便从配置对象中查找模型路径

目前,我使用一个名为formData的预初始化二维数组进行此工作,我将每个表单模型绑定到v-model="formData[rowIndex][formIndex]"并观察该对象的更改并更新父数据对象,但是我不喜欢这种方法,因为它需要我为动态字段添加预先初始化一个数组。

我需要 2 级嵌套,因为我在另一个组件上使用这个表单构建器组件,该组件需要设置一个看起来像的对象

data: {
  templates: {
    operatingSystems: {
      <someuuid1>: [ <osid1>, <osid2> ],
      <someuuid2>: [ <osid5>, <osid10>, <osid22> ]
   }
  }
}

我的路径字符串看起来像

templates.operatingSystems[<dynamic uuid>]

更新 2

我从使用多维数组更改为带有键名的普通对象

"<rowIndex>_<formIndex>"

并使用深度监视使数据与父级保持同步。 我仍然认为插入式绑定是有益的。

+1

对我来说, v-model="$data[field.name]"可以解决问题!

@victorwpbastos这不适用于设置深度嵌套的对象,因为它只会使用 field.name 作为键

例如,如果您有以下数据和字段字符串

$data = {
  'animal': {
    'dog': {
      'husky': 1
    }
  }
}
field.name = 'animal.dog.husky'

你用

v-model="$data[field.name]"

并在表单上输入 2 的值,数据最终看起来像这样

$data = {
  'animal': {
    'dog': {
      'husky': 1
    }
  },
 'animal.dog.husky': 2
}

内插绑定有用的原因是您在构建动态嵌套输入时无法将父路径(例如“animal.dog”)“硬编码”到指令中

我重新审视了这个发现一个更简单的解决方案。 您可以创建一个自定义对象,并在使用模型路径字符串创建时向其添加 getter/setter。 这是一个简单的例子

输入列表

<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>

正在使用

<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>

如果使用这种方式,我们可以通过什么方式为每个动态创建的反应数据设置观察者?

@luqmanrom我不熟悉 vue watcher 的内部工作原理,但我相信使用 vue.set 创建的任何内容都可以被监视,因此您可以添加一些代码来监视动态道具并在更改时发出事件,或者您可以查看目标对象。 其他人可能有更好的建议

我为此编写了一个工具包。 还允许您使用 v-model 改变 vuex

https://github.com/bhoriuchi/vue-deepset

这应该可以解决问题:

指示

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);
    }
});

用法(组件)

const component = Vue.extend({
    template: `<input v-deep-model="'one.two.three'">`,
    data() {
        return {
            one: { two: { three: 'foo' } }
        };
    }
});

这是要点参考

嗨,这里的任何机构。 我在 Laravel 中使用 VUE.js。 我有来自数据库的动态自定义表单字段。 我关注了@yyx990803 。 v-model="form['name']"。 该领域工作。 但问题是我无法在 laravel 控制器中获取字段值。 有人在吗。 我正在使用@tylerOtwell Form.js 类。
对你的帮助表示感谢。
谢谢

这不是一个帮助论坛。 我们有一个专门用于回答问题的https://forum.vuejs.org

我真的很难尝试调用一个函数来找出v-model值。 这是一个例子。

我正在尝试构建一个看起来像这样的日期范围选择器。
image

在这里,预设来自一个看起来像这样的数组..

presets = [
  {
    label: 'Today',
    range: [moment().format('YYYY-MM-DD'), moment().format('YYYY-MM-DD')]
  },
]

现在,我的data组件中的这些输入字段也有两个日期。 startDate & endDate

我真正想做的是将用户选择的日期与我的预设配置中传递的日期进行比较,并将v-model值设置为truefalse但我无法因为...

  • v-model不接受条件,所以我不能做preset.range[0] === startDate && preset.range[1] === endDate
  • v-model不允许将v-for别名传递给函数。 所以我不能做类似的事情
<li v-model="isActive(index)" v-for="(preset, index) in presets">
...
</li>

至少允许有条件语句可以轻松解决这个问题。

另外,我可能在做一些根本错误的事情,所以请指出我是否可以以任何不同的方式实现它。

目前我已经通过利用计算属性是函数调用这一事实解决了这个问题。

脚本

computed: {
  isActive() {
      return this.presets.map(
        preset =>
          preset.range[0] === this.startDate && preset.range[1] === this.endDate
      );
    }
}

模板

<li v-model="isActive[index]" v-for="(preset, index) in presets">
...
</li>

但这对我来说真的像是一个黑客。 没有把握。 请建议。

有人知道这是否也可以与此处解释的 Vuex 结合使用? https://vuex.vuejs.org/guide/forms.html

我想要一个有点动态的输入字段。

<input v-model="dataHandler" :scope="foo" type="checkbox" />

如何在以下代码中访问 dom 元素的“范围”?

computed: {
  message: {
    get () {
      //
    },
    set (value) {
      //
    }
  }
}

@vielhuber尝试使用ref吗?

<input ref="myInput" v-model="dataHandler" :scope="foo" type="checkbox" />
this.$refs.myInput.getAttribute('scope') // => 'foo'

嗨,我有一个与此主题相关的 Vue 问题 - “动态 v-model 指令”:

当我实现 Vue 组件时,如何动态控制 v-model 修饰符 - .lazy等?
例如:

<el-input v-model[field.lazy ? '.lazy' : '']="model[field.key]">

这对我有用。

<input v-model="$data[field].key" type="text">

@fritx为了“动态”更改修饰符,我使用了这样的 v-if 导演。

<input v-if="field.lazy" v-model.lazy="model[field.key]">
<input v-else v-model="model[field.key]">

但是,如果您想要多种修饰符的多种组合,这会变得很麻烦。

我想一种选择可能是创建一个可重用的组件,其中包含所有 if 语句并将其传递给您要呈现的输入组件以及决定呈现具有所需修饰符的输入的修饰符数组。 使用上面的 if 语句对我来说已经足够了。

我找不到在 v-model 指令中动态访问计算属性的方法。
我无法访问我的计算属性,因为您可以访问数据属性
v-model="$data[something]"

我的代码是这样的:

computed: { get () { // }, set (value) { // } } }

我需要使用字符串访问计算属性的方法,但我找不到。
这是一个示例,但不同的解决方案也适用。
<input v-model="$computed[someDynamicString]">
要不就
<input v-model="[someDynamicString]">

我发现的最接近的东西是“_computedWatchers[someDynamicString].value”,但这不适用于 setter 和 getter,如果它只是一个计算值,它可能会起作用。

v-model="dialogTemp.tmBasFuelEntities[dialogTemp.tmBasFuelEntities.findIndex(t=>t.paramCode==item.paramCode)].paramValue"

这是我的 dialogTemp:

dialogTemp: {
  tmBasFuelEntities: [
    {
      paramCode: '',
      paramValue: ''
    },
    {
      paramCode: '',
      paramValue: ''
    },
    {
      paramCode: '',
      paramValue: ''
    },
  ]
}

@fritx为了“动态”更改修饰符,我使用了这样的 v-if 导演。

<input v-if="field.lazy" v-model.lazy="model[field.key]">
<input v-else v-model="model[field.key]">

但是,如果您想要多种修饰符的多种组合,这会变得很麻烦。

我想一种选择可能是创建一个可重用的组件,其中包含所有 if 语句并将其传递给您要呈现的输入组件以及决定呈现具有所需修饰符的输入的修饰符数组。 使用上面的 if 语句对我来说已经足够了。

这很酷,但我不得不将很多道具传递给如此冗长的道具,知道吗? @丹汉森

<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您可以将v-model更改:value / @input并手动解析它。

</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看起来很棒,伙计

@danhanson恐怕应该是:

:value="getFieldValue(field, model[field.key])"
@input="model[field.key] = getFieldValue(field, $event)"
@change="model[field.key] = getFieldValue(field, $event)"

我不确定,我会试试。 谢谢!

@尼诺约维奇

我在这里找到了一个解决方案: https :

<input v-model="_self[someDynamicString]">
为我工作

像这样的东西

<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;'
        },
      },
    ],
  },
]

当字段类型为Number ,我想使用v-model.number ,这样更方便。 @fritx

我拆解了 v-model 以适应它。

<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>

我使用 jquery 插入的(某些部分用于表单输入)克隆了 HMTL。 (不要说我为什么使用 jquery)。 现在我的元素正在被 jquery 插入。 那么是否可以绑定v-model。

$('.area').append('formPart')
in form i have some inputs like
<div class="form-group">
<input type="text" name="area2" /> 
<input type="text" name="area3" />
</div>

那么我如何在区域 2 和 3 上绑定 v-model。

@尼诺约维奇

我在这里找到了一个解决方案: https :

<input v-model="_self[someDynamicString]">
为我工作

也适用于我,但“_self”变量是为 Vue 的内部属性保留的(参见 #2098)。

换句话说,这个实现在未来可能会中断。

我更喜欢这种方式:

<template>
  <input v-model="mySelf[someDynamicString]">
</template>

<script>
export default {
  data() {
    return {
      mySelf: this
    }
  }
}
</script>

有关更多详细信息,请参阅: https :

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

guan6 picture guan6  ·  3评论

franciscolourenco picture franciscolourenco  ·  3评论

robertleeplummerjr picture robertleeplummerjr  ·  3评论

hiendv picture hiendv  ·  3评论

seemsindie picture seemsindie  ·  3评论