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 format HTML. Le composant a un input[type=text]
. Je veux que cette valeur se reflète sur le parent (instance principale 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 ça ne marche pas.
La liaison bidirectionnelle est déconseillée dans Vue 2.0 au profit d'une architecture davantage axée sur les événements. En général, un enfant ne doit pas muter ses accessoires. Au contraire, il devrait $emit
et laisser le parent répondre à ces événements.
Dans votre cas spécifique, vous pouvez utiliser un composant personnalisé avec _v-model
_. Il s'agit d'une syntaxe spéciale qui permet une liaison proche de la liaison bidirectionnelle, mais constitue en réalité un raccourci pour l'architecture pilotée par les événements 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 docs affirment 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 l'accessoire sur l'enfant doit être nommé value et que l'enfant doit $ émettre un événement nommé input
.
De la documentation :
Dans Vue.js, la relation composant parent-enfant peut être résumée en tant qu'accessoires en bas, événements en haut. Le parent transmet les données à l'enfant via des accessoires, et celui-ci envoie des messages au parent via des événements. Voyons comment ils fonctionnent ensuite.
Voici le code pour transmettre les accessoires à un élément enfant:
<div>
<input v-model="parentMsg">
<br>
<child v-bind:my-message="parentMsg"></child>
</div>
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
}
}
})
Dans le composant enfant: this.$emit('eventname', this.variable)
Dans le composant parent:
<component @eventname="updateparent"></component>
methods: {
updateparent(variable) {
this.parentvariable = variable
}
}
Composant enfant
Utilisez this.$emit('event_name')
pour envoyer un événement au composant parent.
Composant parent
Afin d'écouter cet événement dans le composant parent, nous faisons v-on:event_name
et une méthode (ex. handleChange
) que nous souhaitons exécuter lorsque cet événement se produit
Terminé :)
Je suis d’accord avec l’événement qui émet et les réponses en v-modèle pour les précédents. Cependant, je pensais publier ce que je trouvais à propos des composants contenant plusieurs éléments de formulaire souhaitant être renvoyés à leur parent, ce qui semble être l’un des premiers articles renvoyés par Google.
Je sais que la question spécifie une seule entrée, mais cela semblait être la correspondance la plus proche et pourrait permettre aux gens de gagner un peu de temps avec des composants similaires vue. En outre, personne n'a encore mentionné le modificateur .sync
.
Autant que je sache, la solution v-model
convient uniquement à une entrée renvoyée à son parent. J'ai pris 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).
C'est ce qu'on appelle le modificateur .sync
.
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 pour le parent et pour l'enfant.
C’est pourquoi nous recommandons plutôt d’émettre des événements selon le modèle
update:myPropName
. Par exemple, dans un composant hypothétique avec untitle
prop, 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 locale 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 fichiers à la fois en les envoyant via un objet. Découvrez le documentation ici
Il est également possible de transmettre 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>
Le moyen le plus simple est d'utiliser this.$emit
Father.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>
Child.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