vuejs met à jour les données parent à partir du composant enfant


155

Je commence à jouer avec vuejs (2.0). J'ai construit une page simple avec un composant. La page a une instance Vue avec des données. Sur cette page, je me suis inscrit et j'ai ajouté le composant au html. Le composant en a un input[type=text]. Je veux que cette valeur se reflète sur le parent (instance principale de Vue).

Comment mettre à jour correctement les données parent du composant? Passer un accessoire lié du parent n'est pas bon et jette quelques avertissements à la console. Ils ont quelque chose dans leur doc mais cela ne fonctionne pas.


1
Pouvez-vous ajouter le code que vous avez essayé, qui ne fonctionne pas.
Saurabh

Réponses:


181

La liaison bidirectionnelle est obsolète dans Vue 2.0 au profit d'une architecture plus orientée événement. En général, un enfant ne doit pas muter ses accessoires. Au contraire, il devrait les $emitévénements et laisser le parent répondre à ces événements.

Dans votre cas spécifique, vous pouvez utiliser un composant personnalisé avec v-model. C'est une syntaxe spéciale qui permet quelque chose de proche de la liaison bidirectionnelle, mais qui est en fait un raccourci pour l'architecture événementielle décrite ci-dessus. Vous pouvez en savoir plus ici -> https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events .

Voici un exemple simple:

Vue.component('child', {
  template: '#child',
  
  //The child has a prop named 'value'. v-model will automatically bind to this prop
  props: ['value'],
  methods: {
    updateValue: function (value) {
      this.$emit('input', value);
    }
  }
});

new Vue({
  el: '#app',
  data: {
    parentValue: 'hello'
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>

<div id="app">
  <p>Parent value: {{parentValue}}</p>
  <child v-model="parentValue"></child>
</div>

<template id="child">
   <input type="text" v-bind:value="value" v-on:input="updateValue($event.target.value)">
</template>


Les documents indiquent que

<custom-input v-bind:value="something" v-on:input="something = arguments[0]"></custom-input>

est équivalent à

<custom-input v-model="something"></custom-input>

C'est pourquoi le prop sur l'enfant doit être nommé value, et pourquoi l'enfant doit $ émettre un événement nommé input.


premier merci pour la réponse. pouvez-vous s'il vous plaît développer, ou mieux pointer vers des documents sur l'événement «entrée»? cela ressemble à un événement intégré.
Gal Ziv

J'ai ajouté une clarification et rendu le lien vers la documentation plus évident.
asemahle

1
J'ai omis la "valeur" de l'accessoire pour le composant et la fonction créée et cela fonctionne toujours. pouvez-vous expliquer pourquoi vous l'avez utilisé?
xetra11

1
Si vous n'ajoutez pas l'accessoire, ce sera undefinedjusqu'au premier changement. Voir ce violon où j'ai commenté props: ['value']. Notez comment la valeur initiale est undefined, au lieu de hello: jsfiddle.net/asemahle/8Lrkfxj6 . Après le premier changement, Vue ajoute dynamiquement un accessoire de valeur au composant, donc cela fonctionne.
asemahle

J'ai lu juste après cela dans les documents. Excellent exemple de travail. +10 si je pouvais!
terre battue

121

De la documentation :

Dans Vue.js, la relation entre les composants parent-enfant peut être résumée en tant que props down, events up. Le parent transmet les données à l'enfant via des accessoires, et l'enfant envoie des messages au parent via des événements. Voyons comment ils fonctionnent ensuite.

entrez la description de l'image ici

Comment passer des accessoires

Voici le code pour passer des accessoires à un élément enfant:

<div>
  <input v-model="parentMsg">
  <br>
  <child v-bind:my-message="parentMsg"></child>
</div>

Comment émettre un événement

HTML:

<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>

JS:

Vue.component('button-counter', {
  template: '<button v-on:click="increment">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    increment: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})

5
Et si la fonction «incrément» était dans le composant parent et que je voulais la déclencher à partir du composant enfant?
Hamzaouiii

le moment de cisaillement de saisir le concept, bien que l'ayant utilisé plusieurs fois par copier-coller piraté ...
Yordan Georgiev

1
Je vais imprimer ce graphique et le coller sur ma tête. THX!
domih

1
Dans cet exemple, ne devrions-nous pas avoir un écouteur d'événement défini dans le composant racine? Quelque chose comme: `` `monté () {this.on ('increment', () => {this.incrementTotal ();}); } `` ``
jmk2142

94

Dans le composant enfant:

this.$emit('eventname', this.variable)

Dans le composant parent:

<component @eventname="updateparent"></component>

methods: {
    updateparent(variable) {
        this.parentvariable = variable
    }
}

3
Mon esprit est tout simplement époustouflé par cet exemple. Vous n'avez aucune idée du nombre de tutoriels que j'ai
suivis

18

Composant enfant

Permet this.$emit('event_name')d'envoyer un événement au composant parent.

entrez la description de l'image ici

Composant parent

Afin d'écouter cet événement dans le composant parent, nous faisons v-on:event_nameet une méthode ( ex. handleChange) que nous voulons exécuter sur cet événement se produit

entrez la description de l'image ici

Terminé :)


13

Je suis d'accord avec l'émission d'événements et les réponses du modèle v pour ceux ci-dessus. Cependant, j'ai pensé publier ce que j'ai trouvé sur les composants avec plusieurs éléments de formulaire qui veulent être renvoyés à leur parent, car cela semble l'un des premiers articles renvoyés par Google.

Je sais que la question spécifie une seule entrée, mais cela semblait la correspondance la plus proche et pourrait faire gagner du temps aux gens avec des composants de vue similaires. De plus, personne n'a encore mentionné le .syncmodificateur.

Autant que je sache, la v-modelsolution n'est adaptée qu'à une seule entrée retournant à leur parent. J'ai mis un peu de temps à le chercher, mais la documentation de Vue (2.3.0) montre comment synchroniser plusieurs accessoires envoyés dans le composant au parent (via emit bien sûr).

Il est appelé à .syncjuste titre le modificateur.

Voici ce que dit la documentation :

Dans certains cas, nous pouvons avoir besoin d'une «liaison bidirectionnelle» pour un accessoire. Malheureusement, une véritable liaison bidirectionnelle peut créer des problèmes de maintenance, car les composants enfants peuvent muter le parent sans que la source de cette mutation soit évidente à la fois chez le parent et l'enfant.

C'est pourquoi à la place, nous vous recommandons d'émettre des événements dans le modèle de update:myPropName. Par exemple, dans un composant hypothétique avec un titleaccessoire, nous pourrions communiquer l'intention d'attribuer une nouvelle valeur avec:

this.$emit('update:title', newTitle)

Ensuite, le parent peut écouter cet événement et mettre à jour une propriété de données locales, s'il le souhaite. Par exemple:

<text-document   
 v-bind:title="doc.title"  
 v-on:update:title="doc.title = $event"
></text-document>

Pour plus de commodité, nous proposons un raccourci pour ce modèle avec le modificateur .sync:

<text-document v-bind:title.sync="doc.title"></text-document>

Vous pouvez également synchroniser plusieurs à la fois en envoyant via un objet. Consultez la documentation ici


C'est ce que je cherchais. Merci beaucoup.
Thomas

C'est la meilleure solution et la plus à jour en 2020. Merci beaucoup!
Marcelo le

6

Le moyen le plus simple est d'utiliser this.$emit

Père.vue

<template>
  <div>
    <h1>{{ message }}</h1>
    <child v-on:listenerChild="listenerChild"/>
  </div>
</template>

<script>
import Child from "./Child";
export default {
  name: "Father",
  data() {
    return {
      message: "Where are you, my Child?"
    };
  },
  components: {
    Child
  },
  methods: {
    listenerChild(reply) {
      this.message = reply;
    }
  }
};
</script>

Enfant.vue

<template>
  <div>
    <button @click="replyDaddy">Reply Daddy</button>
  </div>
</template>

<script>
export default {
  name: "Child",
  methods: {
    replyDaddy() {
      this.$emit("listenerChild", "I'm here my Daddy!");
    }
  }
};
</script>

Mon exemple complet: https://codesandbox.io/s/update-parent-property-ufj4b


5

Il est également possible de passer des accessoires en tant qu'objet ou tableau. Dans ce cas, les données seront liées dans les deux sens:

(Ceci est noté à la fin du sujet: https://vuejs.org/v2/guide/components.html#One-Way-Data-Flow )

Vue.component('child', {
  template: '#child',
  props: {post: Object},
  methods: {
    updateValue: function () {
      this.$emit('changed');
    }
  }
});

new Vue({
  el: '#app',
  data: {
    post: {msg: 'hello'},
    changed: false
  },
  methods: {
    saveChanges() {
        this.changed = true;
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>

<div id="app">
  <p>Parent value: {{post.msg}}</p>
  <p v-if="changed == true">Parent msg: Data been changed - received signal from child!</p>
  <child :post="post" v-on:changed="saveChanges"></child>
</div>

<template id="child">
   <input type="text" v-model="post.msg" v-on:input="updateValue()">
</template>



0

1) Child Compnent: vous pouvez utiliser comme ceci dans l'écriture de composant enfant comme ceci: this.formValue est d'envoyer des données au composant parent

this.$emit('send',this.formValue)

2) Parrent Compnenet: et dans la balise de composant parrent, recevez une variable comme celle-ci: et c'est le code pour recevoir les données de cet enfant dans la balise de composant parent

@send="newformValue"

0

Une autre façon est de passer une référence de votre setter du parent comme accessoire au composant enfant, comme ils le font dans React. Dites, vous avez une méthode updateValuesur le parent de mettre à jour la valeur, vous pouvez instancier le composant enfant comme ceci: <child :updateValue="updateValue"></child>. Ensuite , sur l'enfant , vous aurez un accessoire correspondant: props: {updateValue: Function}et dans le modèle appeler la méthode lorsque l'entrée change: <input @input="updateValue($event.target.value)">.


0

Je ne sais pas pourquoi, mais je viens de mettre à jour avec succès les données parentales en utilisant les données comme objet, :set&computed

Parent.vue

<!-- check inventory status - component -->
    <CheckInventory :inventory="inventory"></CheckInventory>

data() {
            return {
                inventory: {
                    status: null
                },
            }
        },

Enfant.vue

<div :set="checkInventory">

props: ['inventory'],

computed: {
            checkInventory() {

                this.inventory.status = "Out of stock";
                return this.inventory.status;

            },
        }

0

son exemple vous dira comment transmettre la valeur d'entrée au parent sur le bouton d'envoi.

Définissez d'abord eventBus en tant que nouveau Vue.

//main.js
import Vue from 'vue';
export const eventBus = new Vue();

Pass your input value via Emit.
//Sender Page
import { eventBus } from "../main";
methods: {
//passing data via eventbus
    resetSegmentbtn: function(InputValue) {
        eventBus.$emit("resetAllSegment", InputValue);
    }
}

//Receiver Page
import { eventBus } from "../main";

created() {
     eventBus.$on("resetAllSegment", data => {
         console.log(data);//fetching data
    });
}

0

Je pense que cela fera l'affaire:

@change="$emit(variable)"

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.