236 votes

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

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

Comment mettre correctement à jour les données parentales du composant ? Le passage d'une prop liée depuis le parent n'est pas bon et envoie des avertissements à la console. Il y a quelque chose dans leur doc mais cela ne fonctionne pas.

2 votes

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

0 votes

Cette communication peut être établie par : emit event ou vuex.

249voto

asemahle Points 7443

La liaison bidirectionnelle a été supprimée dans Vue 2.0 au profit d'une architecture plus événementielle. En général, un enfant ne doit pas modifier ses accessoires. Il doit plutôt $emit et laisser le parent répondre à ces événements.

Dans votre cas spécifique, vous pourriez utiliser un composant personnalisé avec v-model . Il s'agit d'une syntaxe spéciale qui permet de se rapprocher de la liaison bidirectionnelle, mais qui est en fait un raccourci pour l'architecture événementielle décrite ci-dessus. Vous pouvez lire à ce sujet 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 précisent 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 la prop sur l'enfant doit être nommée value, et pourquoi l'enfant doit $emiter un événement nommé input .

0 votes

Tout d'abord, merci pour la réponse. pouvez-vous s'il vous plaît développer, ou mieux pointer vers des documents sur l'événement 'input' ? il semble que ce soit un événement intégré.

2 votes

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

2 votes

J'ai omis la prop "value" pour le composant et la fonction créée et cela fonctionne toujours. pouvez-vous expliquer pourquoi vous l'avez utilisée ?

219voto

Dans le composant enfant :

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

Dans le composant parent :

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

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

34 votes

Je suis tout simplement époustouflé par cet exemple. Vous n'avez pas idée du nombre de tutoriels que j'ai parcourus avant d'en arriver là...

0 votes

@Sarvar Nishonboev - lorsque la méthode updateparent met à jour la variable parent, la transmet-elle "automatiquement" à l'enfant en tant que prop ?

1 votes

@veritas - non, pas dans ce cas car le composant enfant ne reçoit pas parentvariable comme prop. S'il le faisait, il le ferait.

155voto

Saurabh Points 29563

De la documentation :

Dans Vue.js, la relation entre le composant parent et l'enfant peut être résumée comme suit : props vers le bas, événements vers le haut. Le parent transmet des données à l'enfant via les props, et l'enfant envoie des messages au parent via les événements. Voyons maintenant comment ils fonctionnent.

enter image description here

Comment passer les accessoires

Voici le code pour passer les props à 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
    }
  }
})

6 votes

Que se passe-t-il si la fonction "incrément" se trouve dans le composant parent et que je veux la déclencher à partir du composant enfant ?

1 votes

Le moment de cisaillement de saisir le concept, bien qu'il ait été utilisé plusieurs fois par des copier-coller bricolés ...

2 votes

Je vais imprimer ce graphique et le coller sur ma tête. Merci.

25voto

Archernar Points 83

Je suis d'accord avec les réponses concernant l'émission d'événements et le modèle en V pour les réponses ci-dessus. Cependant, j'ai pensé que je posterais ce que j'ai trouvé sur les composants avec plusieurs éléments de formulaire qui veulent émettre vers leur parent puisque cela semble être l'un des premiers articles retournés par Google.

Je sais que la question spécifie une seule entrée, mais cela semblait être la plus proche et pourrait faire gagner du temps aux gens qui utilisent des composants Vue similaires. Par ailleurs, personne n'a mentionné le .sync encore le modificateur.

Pour autant que je sache, le v-model n'est adaptée qu'à une entrée retournant à son parent. J'ai pris un peu de temps pour le chercher, mais la documentation de Vue (2.3.0) montre comment synchroniser plusieurs props envoyés dans le composant pour les renvoyer au parent (via emit bien sûr).

On l'appelle à juste titre le .sync modificateur.

Voici ce que le documentation dit :

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, parce que les composants enfants peuvent muter le parent sans que la source de sans que la source de cette mutation soit évidente à la fois pour le parent et pour l'enfant.

C'est pourquoi nous recommandons plutôt d'émettre des événements selon le modèle suivant update:myPropName . Par exemple, dans un composant hypothétique avec un title nous pourrions communiquer l'intention d'assigner une nouvelle valeur avec :

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

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

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

Par 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 en synchroniser plusieurs à la fois en les envoyant par le biais d'un objet. Consultez la page documentation ici

0 votes

C'est ce que je cherchais. Merci beaucoup.

0 votes

Il s'agit de la meilleure solution, la plus récente, en date de 2020. Merci beaucoup !

7voto

Perlovka Points 31

Il est également possible de passer des props sous forme d'objet ou de 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>

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