127 votes

v-modèle et les composants enfants ?

J'ai un formulaire et je lie une entrée en utilisant v-model :

<input type="text" name="name" v-model="form.name">

Maintenant, je veux extraire l'entrée et en faire un composant propre, comment lier les valeurs du composant enfant à l'objet parent ? form.name ?

3 votes

276voto

thanksd Points 25423

Comme indiqué dans la documentation ,

v-model est un sucre syntaxique :

<input
 v-bind:value="something"
 v-on:input="something = $event.target.value">

Pour mettre en œuvre la v-model pour un composant personnalisé :

  • spécifier un value prop pour le composant
  • faire une propriété calculée avec un setter calculé pour la valeur interne (puisque vous ne devez pas modifier la valeur d'une prop à l'intérieur d'un composant).
  • définir un get pour la propriété calculée, qui renvoie la méthode value valeur du bien
  • définir un set pour la propriété calculée qui émet un message input avec la valeur mise à jour chaque fois que la propriété change

Voici un exemple simple :

Vue.component('my-input', {
  template: `
    <div>
      My Input:
      <input v-model="inputVal">
    </div>
  `,
  props: ['value'],
  computed: {
    inputVal: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit('input', val);
      }
    }
  }
})

new Vue({
  el: '#app',
  data() {
    return { 
      foo: 'bar' 
    }
  }
})

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
  <!-- using v-model... -->
  <my-input v-model="foo"></my-input>

  <!-- is the same as this... -->  
  <my-input :value="foo" @input="foo = $event"></my-input>

  {{ foo }}
</div>

Merci à @kthornbloom pour avoir repéré un problème avec l'implémentation précédente.

Changements de rupture dans Vue 3

Selon la documentation Dans Vue 3, des changements importants ont été apportés à l'implémentation du v-model :

  • value -> modelValue
  • input -> update:modelValue

0 votes

Merci - comment récupérez-vous la valeur émise de l'entrée enfant dans le parent ?

2 votes

v-model le fait automatiquement, en fixant la variable liée à la valeur émise. Vous pouvez également l'écouter explicitement via @input sur la balise du composant enfant.

0 votes

Un peu confus avec v-model, dois-je mettre cela sur la déclaration du composant enfant à l'intérieur du composant parent ? Je veux lier l'entrée du composant enfant à une valeur du composant parent.

119voto

Cameron Wilby Points 410

Précisez le :value et @input dans le composant enfant, et tirer parti de la fonction v-model dans le composant parent.

Vue 2

MonEntrée.vue

<template>
  <input 
    :value="value" 
    @input="$emit('input', $event.target.value)" />
</template>

<script>
export default {
  props: ['value']
};
</script>

Écran.vue

<template>
  <my-input v-model="name" />
</template>

<script>
import MyInput from './MyInput.vue';

export default {
  components: { MyInput },

  data: () => ({
    name: ''
  })
};
</script>

Vue 3

MonEntrée.vue

<template>
  <input 
    :value="modelValue" 
    @input="$emit('update:modelValue', $event.target.value)" />
</template>

<script>
export default {
  props: ['modelValue']
};
</script>

Écran.vue

<template>
  <my-input v-model="name" />
</template>

<script>
import MyInput from './MyInput.vue';

export default {
  components: { MyInput },

  data: () => ({
    name: ''
  })
};
</script>

10 votes

Cela devrait être la réponse acceptée. C'est la version la plus simple et la plus directe. Il n'est pas nécessaire de surveiller ou de copier des données dans le composant enfant lorsque tout ce que vous devez faire est de passer par un v-modèle.

3 votes

La réponse la plus simple, et de loin

4 votes

C'est la réponse.

27voto

Benoit Blanchon Points 2252

Solution pour Vue 2

Vous pouvez transférer tous les attributs et listeners (y compris v-model ) du parent à l'enfant comme suit :

<input v-bind="$attrs" v-on="$listeners" />

Voici le documentation pour $attrs :

Contient des liaisons d'attributs de portée parentale (à l'exception de class et style ) qui ne sont pas reconnus (et extraits) comme des props. Lorsqu'un composant n'a pas de props déclaré, il contient essentiellement tous les liens de portée parentale (à l'exception de class et style ), et peut être transmis à un composant interne par l'intermédiaire de v-bind=" $attrs" - utile lors de la création de composants d'ordre supérieur .

Veillez à définir inheritAttrs à false pour éviter que les attributs soient appliqués à l'élément racine (par défaut, tous les attributs sont appliqués à l'élément racine).

Voici le documentation pour $listeners :

Contient des écouteurs d'événements v-on de portée parentale (sans .native modificateurs). Cette information peut être transmise à un composant interne par le biais de la fonction v-on="$listeners" - utile lors de la création de composants enveloppants transparents .

Parce que v-model est juste un raccourci pour v-bind + v-on il est également transféré.

Notez que cette technique est disponible depuis Vue 2.4.0 (juillet 2017), où cette fonctionnalité est décrite comme "Création plus facile de composants wrappers".

Solution pour Vue 3

Vue 3 a retiré le $listeners car les écouteurs se trouvent maintenant dans l'objet $attrs également. Il suffit donc de faire ça :

<input v-bind="$attrs" />

Voici le la documentation pour $attrs :

Contient la portée du parent liaisons et événements d'attributs qui ne sont pas reconnus (et extraits) comme des accessoires de composants ou des événements personnalisés. Lorsqu'un composant n'a pas d'accessoires ou d'événements personnalisés déclarés, il contient essentiellement toutes les liaisons de portée parentale et peut être transmis à un composant interne par l'intermédiaire de v-bind="$attrs" - utile lors de la création de composants d'ordre supérieur.

Si votre composant n'a qu'un seul élément Root (Vue 3 autorise plusieurs éléments Root), alors le paramètre inheritAttrs à false est toujours nécessaire pour éviter que des attributs soient appliqués à l'élément Root.

Voici le la documentation pour inheritAttrs

Par défaut, les liaisons d'attributs de l'étendue parentale qui ne sont pas reconnues comme des props seront "tombées". Cela signifie que lorsque nous avons un composant à racine unique Ces liens seront appliqués à l'élément Root du composant enfant comme des attributs HTML normaux. Lors de la création d'un composant qui englobe un élément cible ou un autre composant, ce n'est pas toujours le comportement souhaité. En définissant inheritAttrs à false Il est possible de désactiver ce comportement par défaut. Les attributs sont disponibles via l'option $attrs et peut être explicitement lié à un élément non-Root à l'aide de la propriété d'instance v-bind .

Une autre différence avec Vue 2 est que le $attrs comprend maintenant class et style .

Voici un extrait de "Disabling Attribute Inheritance" (Désactiver l'héritage des attributs) :

En fixant le inheritAttrs option pour false vous pouvez contrôler l'application à d'autres éléments d'attributs permettant d'utiliser le composant $attrs qui comprend tous les attributs non inclus dans le composant props et emits propriétés (par exemple, class , style , v-on les auditeurs, etc.) .

3 votes

C'est de loin la meilleure réponse - c'est incroyablement facile.

0 votes

Je préfère cette solution à ma réponse, l'héritage des attributs est très utile !

23voto

fitorec Points 521

Utiliser sync dans votre instance principale et si vous utilisez vue > 2.2 vous devez utiliser emit dans le composant.

Vérifiez ce document : - https://alligator.io/vuejs/upgrading-vue-2.3/#propsync

Un exemple simple (avec vue 2.5) :

Vue.component('my-input', {
    template: '<input v-on:keyup="onChange($event)" :value="field"></div>',
    props: ["field"],
    methods: {
        onChange: function (event) {
            this.$emit('update:field', event.target.value);
        }
    }
});

var vm = new Vue({
    el: '#app',
    data:{val: ''},
});

h1 span { color: red }

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>

<div id='app'>
 <h1>
   value
   <span>{{ val }}</span>
 </h1>
    <my-input :field.sync="val">
   </my-input>
 </div>

3voto

r89n Points 21

Pour Vue 3

Le site value prop mentionné dans la réponse acceptée est devenu modelValue et l'événement emit a été modifié en conséquence :

https://v3.vuejs.org/guide/migration/v-model.html#migration-strategy

Je l'ai fait fonctionner en mettant en œuvre la réponse acceptée avec les quelques changements suggérés dans la stratégie de migration.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X