Vue: Dynamische V-Modell-Direktive

Erstellt am 16. Juli 2015  ·  42Kommentare  ·  Quelle: vuejs/vue

Derzeit unterstützt die v-model-Direktive keine Schnurrbart-Typ-Bindungen für Bindungsausdrücke, aber diese Funktion wäre äußerst hilfreich für die Erstellung von Formular-Builder-ähnlichen Implementierungen.

Hilfreichster Kommentar

das kannst du schon mit v-model="form[field.name]" .

Alle 42 Kommentare

Können Sie ein Beispiel nennen, wofür es hilfreich wäre?

Angenommen, wir erstellen einen Klon von phpmyadmin, der Daten von der DESCRIBE TABLE-Anweisung empfängt und aus diesen Daten ein Zeileneditorformular erstellt. Bindungsausdrücke sind in diesem Fall von Natur aus dynamisch, da wir Feldnamen erst nach der Ausführung von SQL DESCRIBE TABLE kennen.

+1, das suche ich auch

+1, hoffe das zu sehen

Ich verstehe immer noch nicht ganz, was dies ermöglicht, was die aktuelle Syntax nicht leisten kann. Vielleicht ein paar Codebeispiele?

Einige Pseudocodes im Zusammenhang mit dem oben beschriebenen phpmyadmin-Klon:

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

das kannst du schon mit v-model="form[field.name]" .

wir können ? Beeindruckend !

Evan, kannst du eine js-Geige aufstellen, die ein todo-artiges Beispiel zeigt?

@yyx990803 , das ist großartig, aber es war nur ein Beispiel, das nur ein Beispiel für die dynamische Verwendung zeigt. Die Logik kann in einer Art von Geschäftsanwendung komplexer sein.

Nur um das klarzustellen, ich bin gegen die Idee, Interpolationen in Direktivenausdrücken zuzulassen. Im Moment bedeutet Schnurrbärte, dass das ausgewertete Ergebnis ein String ist und als String verwendet wird (in das DOM einzufügen oder eine ID-Suche durchzuführen). Das Auswerten von Schnurrbärten in Ausdrücke, die dann ausgewertet werden können, macht es zu zwei Abstraktionsebenen und kann Ihre Vorlagen sehr verwirrend machen.

Ich denke, es wäre sehr wertvoll, die Möglichkeit hinzuzufügen, die Zeichenfolge vor der Auswertung des Ausdrucks zu interpolieren

Die Methode data[pathString] funktioniert gut für Objekte mit 1 verschachtelten Ebene, aber für 2 oder mehr habe ich keine Möglichkeit gefunden, dynamisch zu binden.

Vielleicht füge der Bindung einen Modifikator hinzu, damit sie klarer ist als Schnurrbärte

Beispiel

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

oder vielleicht eine Getter/Setter-Funktion, die übergeben werden kann.

Haftungsausschluss: Ich habe die 2.0-Spezifikation nicht gelesen, daher haben Sie dies möglicherweise dort angesprochen.

@bhoriuchi warum keine berechnete Eigenschaft?

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

Und für v-model Sie die berechnete Eigenschaft mit Setter verwenden.

@simplesmiler Ich habe keine berechneten Eigenschaften in einer Zwei-Wege-Bindung

Aktualisieren

@simplesmiler - Das Problem, auf das ich bei der Verwendung einer berechneten Eigenschaft this innerhalb des Getters oder sogar value in get(value) beide auf die Komponente.

einige Hintergrundinformationen zu meinem Anwendungsfall.

Ich erstelle einen Formular-Builder, der ein Json-Objekt verwendet, um die Formulare zu erstellen. Das Konfigurationsobjekt ist mehr oder weniger ein 2-dimensionales Array von Objekten (Zeilen/Formulare). Jedes Formularkonfigurationsobjekt verfügt über ein Modellfeld, das die Pfadzeichenfolge zu dem Feld enthält, das festgelegt werden soll. Um eine berechnete Eigenschaft dafür zu verwenden, müsste ich in der Lage sein, aus der Komponente mithilfe der Komponentenbindung zu bestimmen, welcher Zeilen- / Formularindex, um den Modellpfad aus dem Konfigurationsobjekt nachzuschlagen

Derzeit arbeite ich mit einem vorinitialisierten zweidimensionalen Array namens formData , an das ich jedes Formularmodell mit v-model="formData[rowIndex][formIndex]" binde, und ich beobachte dieses Objekt auf Änderungen und aktualisiere das übergeordnete Datenobjekt, aber ich Ich mag diesen Ansatz nicht, da ich ein Array für die dynamische Feldaddition vorinitialisieren muss.

Ich benötige 2 Verschachtelungsebenen, weil ich diese Formularerstellungskomponente für eine andere Komponente verwende, die ein Objekt festlegen muss, das in etwa so aussieht

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

wo mein Pfad-String aussehen würde

templates.operatingSystems[<dynamic uuid>]

Update 2

Ich habe von einem mehrdimensionalen Array zu einem einfachen Objekt mit Schlüsselnamen gewechselt

"<rowIndex>_<formIndex>"

und verwendet eine Tiefenüberwachung, um die Daten mit dem Elternteil synchron zu halten. Ich denke immer noch, dass eine interopierte Bindung von Vorteil wäre.

+1

Für mich macht v-model="$data[field.name]" den Trick!

@victorwpbastos Dies funktioniert nicht zum Festlegen von tief verschachtelten Objekten, da nur field.name als Schlüssel verwendet wird

zum Beispiel wenn Sie die folgenden Daten und Feldzeichenfolgen haben

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

und du verwendest

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

und geben Sie den Wert 2 in das Formular ein, die Daten sehen dann so aus

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

Der Grund, warum interpolierte Bindungen nützlich sind, ist, wenn Sie dynamische verschachtelte Eingaben erstellen, bei denen Sie den übergeordneten Pfad (z. B. 'animal.dog') nicht in die Direktive "hart codieren" können

Ich habe dies noch einmal besucht und eine einfachere Lösung gefunden. Sie können ein benutzerdefiniertes Objekt erstellen und diesem beim Erstellen mithilfe der Modellpfadzeichenfolge Getter/Setter hinzufügen. Hier ist ein einfaches Beispiel

Eingabeliste

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

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

Können wir auf diese Weise den Watcher für jede der dynamisch erstellten reaktiven Daten festlegen?

@luqmanrom Ich bin mit dem Innenleben des

Dafür habe ich ein Toolkit geschrieben. ermöglicht es Ihnen auch, vuex mit v-model zu mutieren

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

Das sollte den Trick machen:

Richtlinie

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

Verwendung (Komponente)

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

Hier ist die Gist-Referenz .

Hallo an alle Körper hier. Ich verwende VUE.js mit Laravel. Ich habe dynamische benutzerdefinierte Formularfelder aus der Datenbank. Ich bin @yyx990803 gefolgt. v-model="form['name']". Das Feld funktioniert. Aber das Problem ist, dass ich die Feldwerte in Laravel Controller nicht abrufen kann. Jemand hier. Ich verwende die @tylerOtwell Form.js-Klasse.
Ihre Hilfe wird sehr geschätzt.
Vielen Dank

Dies ist kein Hilfeforum. Wir haben eine spezielle für die Beantwortung von Fragen unter https://forum.vuejs.org

Ich hatte wirklich Mühe, eine Funktion aufzurufen, um den v-model von

Ich versuche, eine Datumsbereichsauswahl zu erstellen, die so aussieht.
image

Hier kommen die Presets aus einem Array, das so aussieht.

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

Jetzt habe ich auch zwei Datumsangaben für diese Eingabefelder in meiner data Komponente. startDate & endDate .

Was ich wirklich tun möchte, ist, das vom Benutzer ausgewählte Datum mit den in meiner voreingestellten Konfiguration übergebenen Datumsangaben zu vergleichen und den v-model Wert entweder auf true oder false aber ich kann es nicht da...

  • v-model akzeptiert keine Bedingungen, daher kann ich preset.range[0] === startDate && preset.range[1] === endDate nicht tun.
  • v-model lässt nicht zu, dass ein v-for Alias ​​an eine Funktion übergeben wird. Also kann ich sowas nicht machen
<li v-model="isActive(index)" v-for="(preset, index) in presets">
...
</li>

Das Zulassen von zumindest bedingten Anweisungen kann dieses Problem leicht lösen.

Außerdem kann es sein, dass ich etwas grundlegend falsch mache, also weisen Sie bitte darauf hin, ob ich es auf andere Weise erreichen könnte.

Derzeit habe ich das Problem gelöst, indem ich die Tatsache ausgenutzt habe, dass berechnete Eigenschaften Funktionsaufrufe sind.

Skript

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

Vorlage

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

Aber es kommt mir wirklich wie ein Hack vor. Nicht sicher. Bitte vorschlagen.

Weiß jemand, ob das auch in Kombination mit Vuex funktioniert, wie hier erklärt? https://vuex.vuejs.org/guide/forms.html

Ich möchte ein Eingabefeld haben, das ein wenig dynamisch ist.

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

Wie kann ich auf den "Bereich" des dom-Elements im folgenden Code zugreifen?

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

@vielhuber versuchen, ref ?

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

Hallo, ich habe eine Vue-Frage zu diesem Thema - "dynamische V-Modell-Direktive":

Wie kann ich beim Implementieren einer Vue-Komponente den V-Modell-Modifikator - .lazy usw. dynamisch steuern?
zum Beispiel:

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

Das funktioniert bei mir.

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

@fritx Um den Modifikator "dynamisch" zu ändern, habe ich den v-if-Direktor so verwendet.

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

Dies kann jedoch umständlich werden, wenn Sie eine große Auswahl an mehreren Kombinationen von Modifikatoren wünschen.

Ich denke, eine Möglichkeit könnte darin bestehen, eine wiederverwendbare Komponente zu erstellen, die alle if-Anweisungen enthält und ihr die Eingabekomponente zu übergeben, die Sie rendern möchten, und das Array von Modifikatoren, die bestimmt, welche Eingabe mit den gewünschten Modifikatoren gerendert wird. Die Verwendung der if-Anweisung wie oben war jedoch gut genug für mich.

Ich konnte den Weg für den dynamischen Zugriff auf berechnete Eigenschaften in der v-model-Direktive nicht finden.
Ich habe keine Möglichkeit, auf meine berechneten Eigenschaften zuzugreifen, da Sie auf Dateneigenschaften mit zugreifen können
v-model="$data[etwas]"

Mein Code ist ungefähr so:

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

Ich brauche die Möglichkeit, mit einer Zeichenfolge auf die berechnete Eigenschaft zuzugreifen, die ich nicht finden konnte.
Dies ist ein Beispiel, aber andere Lösungen würden auch funktionieren.
<input v-model="$computed[someDynamicString]">
oder nur
<input v-model="[someDynamicString]">

Das nächste, was ich gefunden habe, ist "_computedWatchers[someDynamicString].value", aber das funktioniert nicht mit Settern und Gettern, vielleicht würde es funktionieren, wenn es nur ein berechneter Wert wäre.

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

Das ist mein dialogTemp:

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

@fritx Um den Modifikator "dynamisch" zu ändern, habe ich den v-if-Direktor so verwendet.

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

Dies kann jedoch umständlich werden, wenn Sie eine große Auswahl an mehreren Kombinationen von Modifikatoren wünschen.

Ich denke, eine Möglichkeit könnte darin bestehen, eine wiederverwendbare Komponente zu erstellen, die alle if-Anweisungen enthält und ihr die Eingabekomponente zu übergeben, die Sie rendern möchten, und das Array von Modifikatoren, die bestimmt, welche Eingabe mit den gewünschten Modifikatoren gerendert wird. Die Verwendung der if-Anweisung wie oben war jedoch gut genug für mich.

Es ist cool, aber ich musste viele Requisiten an genau diejenige weitergeben, die so ausführlich ist, eine Idee? @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 Sie könnten v-model in :value / @input ändern und es manuell analysieren.

</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 sieht toll aus, Mann

@danhanson Ich fürchte, es sollte sein:

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

Ich bin mir nicht sicher, ich werde es versuchen. Vielen Dank!

@ninojovic

Ich habe hier eine Lösung gefunden: https://forum.vuejs.org/t/accessing-computed-properties-from-template-dynamically/4798/9

<input v-model="_self[someDynamicString]">
funktioniert bei mir

Etwas wie das

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

Wenn der Feldtyp Number , möchte ich v-model.number , was viel bequemer ist. @fritx

Ich Teardown V-Modell, um es zu passen.

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

Ich habe HMTL mit Klonen (einen Teil für die Formulareingabe), die ich mit jquery einfüge. (Sagen Sie nicht, warum ich jquery verwende). jetzt wird mein Element von jquery eingefügt. so ist es möglich, v-Modell zu binden.

$('.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>

Wie kann ich das V-Modell an Bereich 2 und 3 binden.

@ninojovic

Ich habe hier eine Lösung gefunden: https://forum.vuejs.org/t/accessing-computed-properties-from-template-dynamically/4798/9

<input v-model="_self[someDynamicString]">
funktioniert bei mir

Funktioniert bei mir auch, aber die Variable "_self" ist für die internen Eigenschaften von Vue reserviert (siehe # 2098).

Mit anderen Worten, diese Implementierung kann in der Zukunft brechen.

Ich bevorzuge diesen Weg:

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

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

Weitere Details finden Sie unter: https://stackoverflow.com/questions/52104176/use-of-self-attribute-from-vue-vm-is-reliable

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen