Vue: 動的vモデルディレクティブ

作成日 2015年07月16日  ·  42コメント  ·  ソース: vuejs/vue

現在、v-modelディレクティブは、式をバインドするための口ひげタイプのバインディングをサポートしていませんが、この機能は、フォームビルダーのような実装を作成するのに非常に役立ちます。

最も参考になるコメント

あなたはすでにv-model="form[field.name]"それを行うことができます。

全てのコメント42件

それが何に役立つかについて例を挙げていただけますか?

たとえば、phpmyadminのクローンを作成しているとしましょう。このクローンは、DESCRIBE TABLEステートメントからデータを受け取り、そのデータから行エディターフォームを作成します。 この場合、SQL DESCRIBE TABLEを実行した後にのみフィールド名がわかるため、バインディング式は本質的に動的になります。

+1、私もこれを探しています

+1、これを見たい

私はまだこれが現在の構文が達成できないことを可能にするものを完全に理解していません。 多分いくつかのコードサンプル?

上記のphpmyadminクローンに関連するいくつかの擬似コード:

    <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]"それを行うことができます。

私たちはできる ? わお !

エヴァンは、todoっぽい例を示すjsフィドルを立てることができますか

@ yyx990803 、それは素晴らしいですが、それは動的な使用法のほんの一例を示す単なる例でした。 ある種のビジネスアプリケーションでは、ロジックがより複雑になる可能性があります。

明確にするために、ディレクティブ式内で補間を許可するという考えには反対です。 現在、口ひげは、評価された結果が文字列であると予想され、文字列として使用されることを意味します(DOMに挿入されるか、IDルックアップを実行します)。 口ひげを表現に評価して評価できるようにすると、2層の抽象化になり、テンプレートが非常に混乱する可能性があります。

式を評価する前に文字列を補間する機能を追加することは非常に価値があると思います

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

または、渡すことができるゲッター/セッター関数かもしれません。

免責事項:私は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ゲッター内部あるいはvalueget(value)両方の点成分。

私のユースケースの背景。

jsonオブジェクトを使用してフォームを作成するフォームビルダーを作成しています。 構成オブジェクトは、多かれ少なかれオブジェクト(行/フォーム)の2次元配列です。 各フォーム構成オブジェクトには、設定する必要のあるフィールドへのパス文字列を持つモデルフィールドがあります。 このために計算されたプロパティを使用するには、構成オブジェクトからモデルパスを検索するために、どの行/フォームインデックスをバインドするコンポーネントを使用してコンポーネントから決定できる必要があります

現在、 formDataという事前に初期化された2次元配列を使用してこれを機能させており、各フォームモデルを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」など)をディレクティブに「ハードコード」できない動的なネストされた入力を構築する場合です。

私はこれを再検討し、より簡単な解決策を見つけました。 カスタムオブジェクトを作成し、モデルパス文字列を使用して作成したオブジェクトにゲッター/セッターを追加できます。 これが簡単な例です

入力リスト

<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ウォッチャーの内部動作に精通していませんが、vue.setで作成されたものはすべて監視できると思います。コードを追加して動的な小道具を監視し、変更時に偶数を出力したり、ターゲットオブジェクトを監視したりできます。 他の誰かがより良い提案をするかもしれません

このためのツールキットを作成しました。 また、vモデルを使用して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をフォローし@ tylerOtwellForm.jsクラスを使用しています。
あなたの助けは大歓迎です。
ありがとう

これはヘルプフォーラムではありません。 https://forum.vuejs.orgに質問への回答専用のものがあり

v-model値を見つけるために関数を呼び出そうとすると、本当に苦労しました。 これが例です。

このような日付範囲ピッカーを作成しようとしています。
image

ここでは、プリセットは次のような配列から取得されています。

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

これで、コンポーネントのdataにこれらの入力フィールドの日付が2つあります。 startDateendDate

私が本当にやりたいのは、ユーザーが選択した日付をプリセット構成で渡された日付と比較し、 v-model値をtrueまたはfalseすることですが、できませんなぜなら...

  • 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) {
      //
    }
  }
}

@vielhuberref使おうとしますか?

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

こんにちは、このトピックに関連するVueの質問があります-「動的vモデルディレクティブ」:

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

ただし、モディファイアのさまざまな複数の組み合わせが必要な場合は、これが面倒になる可能性があります。

1つのオプションは、すべてのifステートメントを含む再利用可能なコンポーネントを作成し、レンダリングする入力コンポーネントと、目的の修飾子を使用してレンダリングする入力を決定する修飾子の配列を渡すことです。 上記のようなifステートメントを使用することは私にとっては十分でした。

v-modelディレクティブで計算されたプロパティに動的にアクセスする方法が見つかりませんでした。
でデータプロパティにアクセスできるため、計算されたプロパティにアクセスする方法はありません。
v-model = "$ data [something]"

私のコードは次のようなものです:

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

文字列を使用して計算されたプロパティにアクセスする方法が必要ですが、見つかりませんでした。
これは一例ですが、さまざまなソリューションでも機能します。
<input v-model="$computed[someDynamicString]">
あるいは単に
<input v-model="[someDynamicString]">

私が見つけた最も近いものは「_computedWatchers [someDynamicString] .value」ですが、これはセッターとゲッターでは機能しません。計算された値であれば機能する可能性があります。

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

ただし、モディファイアのさまざまな複数の組み合わせが必要な場合は、これが面倒になる可能性があります。

1つのオプションは、すべてのifステートメントを含む再利用可能なコンポーネントを作成し、レンダリングする入力コンポーネントと、目的の修飾子を使用してレンダリングする入力を決定する修飾子の配列を渡すことです。 上記のようなifステートメントを使用することは私にとっては十分でした。

かっこいいですが、とても冗長な小道具にたくさんの小道具を渡さなければなりませんでした。 @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 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)"

よくわかりません、やってみます。 ありがとう!

@ninojovic

私はここで解決策を見つけました: 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モデルを分解します。

<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モデルをバインドすることは可能です。

$('.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モデルをバインドする方法を教えてください。

@ninojovic

私はここで解決策を見つけました: 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 評価