vuejs aggiorna i dati padre dal componente figlio


155

Sto iniziando a giocare con vuejs (2.0). Ho creato una pagina semplice con un componente al suo interno. La pagina ha un'istanza di Vue con dati. In quella pagina ho registrato e aggiunto il componente a HTML. Il componente ne ha unoinput[type=text] . Voglio che quel valore rifletta sul genitore (istanza principale di Vue).

Come aggiorno correttamente i dati del componente principale? Passare un puntello associato dal genitore non è buono e lancia alcuni avvisi alla console. Hanno qualcosa nel loro documento ma non funziona.


1
Puoi aggiungere il codice che hai provato, che non funziona.
Saurabh,

Risposte:


181

L'associazione bidirezionale è stata deprecata in Vue 2.0 a favore dell'utilizzo di un'architettura più guidata dagli eventi. In generale, un bambino non dovrebbe mutare i suoi oggetti di scena. Piuttosto, dovrebbe $emiteventi e lasciare che il genitore risponda a quegli eventi.

Nel tuo caso specifico, puoi utilizzare un componente personalizzato con v-model. Questa è una sintassi speciale che consente qualcosa di simile all'associazione bidirezionale, ma in realtà è una scorciatoia per l'architettura guidata dagli eventi sopra descritta. Puoi leggerlo qui -> https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events .

Ecco un semplice esempio:

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>


I documenti lo affermano

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

è equivalente a

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

Questo è il motivo per cui il prop sul figlio deve essere chiamato value e il motivo per cui il figlio deve emettere un evento chiamato input.


Innanzitutto grazie per la risposta. puoi per favore espandere o meglio indicare documenti sull'evento "input"? sembra un evento integrato.
Gal Ziv,

Ho aggiunto un chiarimento e reso più evidente il collegamento alla documentazione.
Asemahle,

1
Ho omesso il "valore" di prop per il componente e la funzione creata e funziona ancora. puoi spiegare perché l'hai usato?
xetra11

1
Se non aggiungi l'elica, lo sarà undefinedfino alla prima modifica. Vedi questo violino in cui ho commentato props: ['value']. Nota come è il valore iniziale undefined, anziché hello: jsfiddle.net/asemahle/8Lrkfxj6 . Dopo la prima modifica, Vue aggiunge dinamicamente un valore prop al componente, quindi funziona.
asemahle

Ho letto oltre questo nei documenti. Ottimo esempio funzionante. +10 se potessi!
argilla,

121

Dalla documentazione :

In Vue.js, la relazione componente genitore-figlio può essere riassunta come oggetti di scena giù, eventi su. Il genitore trasmette i dati al figlio tramite oggetti di scena e il bambino invia messaggi al genitore tramite eventi. Vediamo come funzionano dopo.

inserisci qui la descrizione dell'immagine

Come passare oggetti di scena

Di seguito è riportato il codice per passare oggetti di scena a un elemento figlio:

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

Come emettere un evento

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
cosa succederebbe se la funzione "incremento" fosse nel componente padre e volessi attivarla dal componente figlio?
Hamzaouiii,

il momento del taglio di cogliere il concetto, anche se lo hanno usato più volte da hacky copia incolla ...
Yordan Georgiev

1
Stamperò quella grafica e me la metto in testa. Grazie!
domih

1
In questo esempio, non dovremmo avere un listener di eventi definito nel componente root? Qualcosa come: `` montato () {this.on ('increment', () => {this.incrementTotal ();}); } ``
jmk2142

94

Nel componente figlio:

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

Nel componente principale:

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

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

3
La mia mente è semplicemente sconvolta da questo esempio. Non hai idea di quanti tutorial ho passato prima di arrivare qui ...
suchislife

18

Componente figlio

Utilizzare this.$emit('event_name')per inviare un evento al componente padre.

inserisci qui la descrizione dell'immagine

Componente principale

Per ascoltare quell'evento nel componente genitore, lo facciamo v-on:event_namee si verifica un metodo ( ex. handleChange) che vogliamo eseguire su quell'evento

inserisci qui la descrizione dell'immagine

Fatto :)


13

Sono d'accordo con l'emissione dell'evento e le risposte del modello v per quelle sopra. Tuttavia, ho pensato di pubblicare ciò che ho trovato sui componenti con più elementi del modulo che vogliono restituire al loro genitore poiché questo sembra uno dei primi articoli restituiti da Google.

So che la domanda specifica un singolo input, ma questa sembrava la corrispondenza più vicina e potrebbe far risparmiare tempo alle persone con componenti VUE simili. Inoltre, nessuno ha ancora menzionato il .syncmodificatore.

Per quanto ne so, la v-modelsoluzione è adatta solo per un input che ritorna al loro genitore. Ho impiegato un po 'di tempo a cercarlo, ma la documentazione di Vue (2.3.0) mostra come sincronizzare più oggetti di scena inviati nel componente al genitore (tramite emit ovviamente).

È appropriatamente chiamato il .syncmodificatore.

Ecco cosa dice la documentazione :

In alcuni casi, potremmo aver bisogno di "rilegatura a due vie" per un sostegno. Sfortunatamente, il vero legame bidirezionale può creare problemi di manutenzione, poiché i componenti figlio possono mutare il genitore senza che la fonte di tale mutazione sia evidente sia nel genitore che nel figlio.

Ecco perché, invece, ti consigliamo di emettere eventi nel modello di update:myPropName. Ad esempio, in un ipotetico componente con un titleprop, potremmo comunicare l'intento di assegnare un nuovo valore con:

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

Quindi il genitore può ascoltare quell'evento e aggiornare una proprietà di dati locali, se lo desidera. Per esempio:

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

Per comodità, offriamo una scorciatoia per questo modello con il modificatore .sync:

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

Puoi anche sincronizzare più alla volta inviando tramite un oggetto. Consulta la documentazione qui


Questo è quello che stavo cercando. Molte grazie.
Thomas,

Questa è la soluzione migliore e più aggiornata al 2020. Grazie mille!
Marcelo,

6

Il modo più semplice è l'uso 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>

Il mio esempio completo: https://codesandbox.io/s/update-parent-property-ufj4b


5

È anche possibile passare oggetti di scena come oggetto o matrice. In questo caso i dati saranno associati a due vie:

(Questo è notato alla fine dell'argomento: 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: è possibile utilizzare in questo modo nel componente figlio scrivere in questo modo: this.formValue è inviare alcuni dati al componente padre

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

2) Parrent Compnenet: e nel tag del componente parrent ricevi la variabile di invio in questo modo: e questo è il codice per ricevere i dati del figlio nel tag del componente genitore

@send="newformValue"

0

Un altro modo è passare un riferimento del tuo setter dal genitore come prop al componente figlio, simile a come lo fanno in React. Dire, si ha un metodo updateValuesul genitore per aggiornare il valore, si potrebbe creare un'istanza del componente figlio in questo modo: <child :updateValue="updateValue"></child>. Poi il bambino si avrà un puntello corrispondente: props: {updateValue: Function}e nel modello chiamare il metodo quando cambia l'ingresso: <input @input="updateValue($event.target.value)">.


0

Non so perché, ma ho appena aggiornato correttamente i dati padre con l'utilizzo di dati come oggetto, :set&computed

Parent.vue

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

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

Child.vue

<div :set="checkInventory">

props: ['inventory'],

computed: {
            checkInventory() {

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

            },
        }

0

il suo esempio ti dirà come passare il valore di input al genitore sul pulsante di invio.

Per prima cosa definisci eventBus come nuovo 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

Penso che questo farà il trucco:

@change="$emit(variable)"

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.