Vue: Directive de modèle v dynamique

Créé le 16 juil. 2015  ·  42Commentaires  ·  Source: vuejs/vue

Actuellement, la directive v-model ne prend pas en charge les liaisons de type moustache pour les expressions de liaison, mais cette fonctionnalité serait extrêmement utile pour créer des implémentations de type constructeur de formulaire.

Commentaire le plus utile

vous pouvez déjà le faire avec v-model="form[field.name]" .

Tous les 42 commentaires

Pouvez-vous donner un exemple de ce à quoi cela serait utile?

Imaginons, par exemple, que nous construisons un clone de phpmyadmin, qui reçoit les données de l'instruction DESCRIBE TABLE et crée un formulaire d'éditeur de lignes à partir de ces données. Les expressions de liaison seront intrinsèquement dynamiques dans ce cas, car nous ne connaîtrons les noms de champs qu'après avoir exécuté SQL DESCRIBE TABLE.

+1, je cherche ça aussi

+1, j'espère voir ça

Je ne comprends toujours pas tout à fait ce que cela permet que la syntaxe actuelle ne puisse pas réaliser. Peut-être des exemples de code ?

Quelques pseudo-codes liés au clone phpmyadmin décrit ci-dessus :

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

vous pouvez déjà le faire avec v-model="form[field.name]" .

nous pouvons ? Wow !

evan pouvez-vous mettre en place un violon js montrant un exemple todo-ish

@yyx990803 , c'est super, mais ce n'était qu'un exemple montrant un seul exemple d'utilisation dynamique. La logique peut être plus complexe dans une sorte d'application métier.

Juste pour être clair, je suis contre l'idée d'autoriser les interpolations à l'intérieur des expressions directive. À l'heure actuelle, les moustaches signifient que le résultat évalué devrait être une chaîne et être utilisé comme une chaîne (à insérer dans le DOM ou à effectuer une recherche d'ID). L'évaluation des moustaches en expressions qui peuvent ensuite être évaluées en fait deux couches d'abstraction et peut finir par rendre vos modèles très déroutants.

je pense qu'il serait très utile d'ajouter la possibilité d'interpoler la chaîne avant d'évaluer l'expression

la méthode data[pathString] fonctionne bien pour les objets avec 1 niveau imbriqué mais pour 2 ou plus, je n'ai pas trouvé de moyen de lier dynamiquement.

peut-être ajouter un modificateur à la reliure pour que ce soit plus clair que les moustaches

Exemple

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

ou peut-être une fonction getter/setter qui peut être transmise.

avis de non-responsabilité : je n'ai pas lu la spécification 2.0, vous avez donc peut-être abordé ce problème ici.

@bhoriuchi pourquoi pas de propriété calculée ?

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

Et pour v-model vous pouvez utiliser la propriété calculée avec setter.

@simplesmiler je n'ai pas essayé les propriétés calculées dans une liaison bidirectionnelle, je

Mettre à jour

@simplesmiler - donc le problème que je rencontre avec l'utilisation d'une propriété calculée est que je n'ai aucun moyen de passer des arguments à la propriété calculée. this à l'intérieur du getter ou même value dans get(value) pointent tous les deux vers le composant.

quelques informations sur mon cas d'utilisation.

Je crée un générateur de formulaires qui utilise un objet json pour créer les formulaires. l'objet de configuration est plus ou moins un tableau d'objets à 2 dimensions (lignes/formes). chaque objet de configuration de formulaire a un champ de modèle qui a la chaîne de chemin d'accès au champ qui doit être défini. afin d'utiliser une propriété calculée pour cela, je devrais être en mesure de déterminer à partir du composant à l'aide de la liaison de composant quel index de ligne/formulaire afin de rechercher le chemin du modèle à partir de l'objet de configuration

actuellement, cela fonctionne à l'aide d'un tableau à 2 dimensions pré-initialisé appelé formData auquel je lie chaque modèle de formulaire avec v-model="formData[rowIndex][formIndex]" et je surveille cet objet pour les changements et met à jour l'objet de données parent, mais je n'aime pas cette approche car elle m'oblige à pré-initialiser un tableau pour l'ajout de champ dynamique.

j'ai besoin de 2 niveaux d'imbrication car j'utilise ce composant de création de formulaire sur un autre composant qui doit définir un objet qui ressemble à quelque chose comme

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

où ressemblerait ma chaîne de chemin

templates.operatingSystems[<dynamic uuid>]

Mise à jour 2

je suis passé d'un tableau multidimensionnel à un objet simple avec des noms de clé

"<rowIndex>_<formIndex>"

et a utilisé une surveillance approfondie pour garder les données synchronisées avec le parent. Je pense toujours qu'une liaison interplaquée serait bénéfique.

+1

Pour moi, v-model="$data[field.name]" fait l'affaire !

@victorwpbastos cela ne fonctionne pas pour définir des objets profondément imbriqués car il utilisera simplement le field.name comme clé

par exemple si vous avez les données et la chaîne de champ suivantes

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

et tu utilises

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

et entrez la valeur 2 sur le formulaire, les données finiraient par ressembler à ceci

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

la raison pour laquelle la liaison interpolée est utile est l'endroit où vous créez des entrées imbriquées dynamiques où vous ne pouvez pas "coder en dur" le chemin parent (par exemple 'animal.dog') dans la directive

J'ai revisité cela a trouvé une solution plus simple. Vous pouvez créer un objet personnalisé et y ajouter des getters/setters lors de la création à l'aide de la chaîne de chemin du modèle. Voici un exemple simple

liste-entrée

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

utilisé

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

Si vous utilisez cette méthode, pouvons-nous définir l'observateur pour chacune des données réactives créées dynamiquement ?

@luqmanrom Je ne connais pas le fonctionnement interne de l'observateur de vue, mais je pense que tout ce qui est créé avec vue.set peut être regardé. Vous pouvez donc ajouter du code pour regarder les accessoires dynamiques et émettre des événements sur les modifications ou vous pouvez regarder l'objet cible. Quelqu'un d'autre pourrait avoir une meilleure suggestion

J'ai écrit une boîte à outils pour cela. vous permet également de muter vuex en utilisant v-model

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

Cela devrait faire l'affaire:

Directif

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

Utilisation (composant)

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

Voici la référence Gist .

Salut tout corps ici. J'utilise VUE.js avec Laravel. J'ai des champs de formulaire personnalisés dynamiques provenant de la base de données. J'ai suivi @yyx990803 . v-model="form['nom']". Le terrain fonctionne. Mais le problème est que je ne peux pas obtenir les valeurs de champ dans le contrôleur laravel. Quelqu'un ici. J'utilise la classe @tylerOtwell Form.js.
Votre aide sera grandement appréciée.
Merci

Ceci n'est pas un forum d'aide. Nous en avons un dédié pour répondre aux questions sur https://forum.vuejs.org

J'ai vraiment eu du mal à faire invoquer une fonction pour trouver la v-model . Voici un exemple.

J'essaie de créer un sélecteur de plage de dates qui ressemble à ceci.
image

Ici, les préréglages proviennent d'un tableau qui ressemble à ceci.

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

Maintenant, j'ai également deux dates pour ces champs de saisie dans mon data de composant. startDate & endDate .

Ce que je veux vraiment faire, c'est comparer la date que l'utilisateur a sélectionnée avec les dates passées dans ma configuration prédéfinie et définir la v-model sur true ou false mais je ne peux pas car...

  • v-model n'accepte pas les conditions donc je ne peux pas faire preset.range[0] === startDate && preset.range[1] === endDate .
  • v-model n'autorise pas la transmission d'alias v-for à une fonction. Donc je ne peux pas faire quelque chose comme
<li v-model="isActive(index)" v-for="(preset, index) in presets">
...
</li>

Permettre d'avoir au moins des instructions conditionnelles peut résoudre ce problème facilement.

De plus, je fais peut-être quelque chose de fondamentalement mal, alors veuillez indiquer si je pourrais y parvenir d'une manière différente.

Actuellement, j'ai résolu le problème en exploitant le fait que les propriétés calculées sont des appels de fonction.

Scénario

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

Modèle

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

Mais cela me semble vraiment être un hack. Pas certain. Veuillez suggérer.

Est-ce que quelqu'un sait si cela fonctionne également en combinaison avec Vuex comme expliqué ici ? https://vuex.vuejs.org/guide/forms.html

Je veux avoir un champ de saisie un peu dynamique.

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

Comment puis-je accéder à la "portée" de l'élément dom dans le code suivant ?

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

@vielhuber essaie d'utiliser ref ?

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

Salut, j'ai une question Vue liée à ce sujet - "directive v-model dynamique":

Lorsque j'implémente un composant Vue, comment puis-je contrôler dynamiquement le modificateur v-model - .lazy , etc ??
par exemple:

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

Cela fonctionne pour moi.

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

@fritx Pour

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

Cela peut cependant devenir fastidieux si vous voulez une grande variété de combinaisons multiples de modificateurs.

Je suppose qu'une option pourrait être de créer un composant réutilisable qui contient toutes les instructions if et de lui transmettre le composant d'entrée que vous souhaitez rendre et le tableau de modificateurs qui détermine quelle entrée avec les modificateurs souhaités est rendue. L'utilisation de l'instruction if comme ci-dessus était assez bonne pour moi.

Je n'ai pas pu trouver le moyen d'accéder dynamiquement à la propriété calculée dans la directive v-model.
Il n'y a aucun moyen pour moi d'accéder à mes propriétés calculées car vous pouvez accéder aux propriétés de données avec
v-model="$data[quelque chose]"

Mon code est quelque chose comme ça :

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

J'ai besoin du moyen d'accéder à la propriété calculée avec une chaîne, que je n'ai pas pu trouver.
Ceci est un exemple, mais différentes solutions fonctionneraient également.
<input v-model="$computed[someDynamicString]">
ou juste
<input v-model="[someDynamicString]">

La chose la plus proche que j'ai trouvée est "_computedWatchers[someDynamicString].value" mais cela ne fonctionne pas avec les setters et les getters, peut-être que cela fonctionnerait s'il ne s'agissait que d'une valeur calculée.

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

Voici mon dialogTemp :

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

@fritx Pour

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

Cela peut cependant devenir fastidieux si vous voulez une grande variété de combinaisons multiples de modificateurs.

Je suppose qu'une option pourrait être de créer un composant réutilisable qui contient toutes les instructions if et de lui transmettre le composant d'entrée que vous souhaitez rendre et le tableau de modificateurs qui détermine quelle entrée avec les modificateurs souhaités est rendue. L'utilisation de l'instruction if comme ci-dessus était assez bonne pour moi.

C'est cool mais j'ai dû passer plein d'accessoires à celui-là même qui est si bavard, une idée ? @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 Vous pouvez changer v-model en :value / @input et l'analyser manuellement.

</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 a l' air super, mec

@danhanson, j'ai bien peur que ce soit :

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

Je ne suis pas sûr, je vais essayer. Merci!

@ninojovic

J'ai trouvé une solution ici : https://forum.vuejs.org/t/accessing-computed-properties-from-template-dynamically/4798/9

<input v-model="_self[someDynamicString]">
travaille pour moi

Quelque chose comme ça

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

Lorsque le type de champ est Number , je souhaite utiliser v-model.number , ce qui est beaucoup plus pratique. @fritx

J'ai démonté le modèle en V pour l'adapter.

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

J'ai clone HMTL en utilisant (une partie pour la saisie de formulaire) que j'insère à l'aide de jquery. (ne dites pas pourquoi j'utilise jquery). maintenant mon élément est inséré par jquery. il est donc possible de lier 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>

Alors, comment puis-je lier v-model sur les zones 2 et 3.

@ninojovic

J'ai trouvé une solution ici : https://forum.vuejs.org/t/accessing-computed-properties-from-template-dynamically/4798/9

<input v-model="_self[someDynamicString]">
travaille pour moi

Fonctionne pour moi aussi, mais la variable "_self" est réservée aux propriétés internes de Vue (voir # 2098).

En d'autres termes, cette implémentation peut casser à l'avenir.

Je préfère cette façon :

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

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

Pour plus de détails, voir : https://stackoverflow.com/questions/52104176/use-of-self-attribute-from-vue-vm-is-reliable

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

franciscolourenco picture franciscolourenco  ·  3Commentaires

wufeng87 picture wufeng87  ·  3Commentaires

seemsindie picture seemsindie  ·  3Commentaires

loki0609 picture loki0609  ·  3Commentaires

bfis picture bfis  ·  3Commentaires