vuejs aktualisieren übergeordnete Daten von der untergeordneten Komponente


154

Ich fange an, mit vuejs (2.0) zu spielen. Ich habe eine einfache Seite mit einer Komponente erstellt. Die Seite hat eine Vue-Instanz mit Daten. Auf dieser Seite habe ich mich registriert und die Komponente zu HTML hinzugefügt. Die Komponente hat eine input[type=text]. Ich möchte, dass dieser Wert sich auf das übergeordnete Element (Hauptinstanz von Vue) auswirkt.

Wie aktualisiere ich die übergeordneten Daten der Komponente korrekt? Das Übergeben einer gebundenen Requisite vom Elternteil ist nicht gut und wirft einige Warnungen an die Konsole. Sie haben etwas in ihrem Dokument, aber es funktioniert nicht.


1
Können Sie den Code hinzufügen, den Sie versucht haben, der nicht funktioniert?
Saurabh

Antworten:


181

Die bidirektionale Bindung wurde in Vue 2.0 zugunsten der Verwendung einer ereignisgesteuerten Architektur abgelehnt. Im Allgemeinen sollte ein Kind seine Requisiten nicht mutieren. Vielmehr sollte es $emitEreignisse geben und die Eltern auf diese Ereignisse reagieren lassen.

In Ihrem speziellen Fall können Sie eine benutzerdefinierte Komponente mit verwenden v-model. Dies ist eine spezielle Syntax, die eine nahezu bidirektionale Bindung ermöglicht, jedoch eine Abkürzung für die oben beschriebene ereignisgesteuerte Architektur darstellt. Sie können hier darüber lesen -> https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events .

Hier ist ein einfaches Beispiel:

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>


Die Dokumente geben das an

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

ist äquivalent zu

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

Aus diesem Grund muss die Requisite für das Kind als Wert bezeichnet werden, und das Kind muss ein Ereignis mit dem Namen $ ausgeben input.


Zunächst danke für die Antwort. Können Sie bitte erweitern oder besser auf Dokumente über das Ereignis "Eingabe" verweisen? es scheint wie ein eingebautes Ereignis.
Gal Ziv

Ich habe eine Klarstellung hinzugefügt und den Link zur Dokumentation deutlicher gemacht.
Asemahle

1
Ich habe den Prop "Wert" für die Komponente und die erstellte Funktion weggelassen und es funktioniert immer noch. Kannst du erklären, warum du es benutzt hast?
Xetra11

1
Wenn Sie die Requisite nicht hinzufügen, erfolgt dies undefinedbis zur ersten Änderung. Sehen Sie sich diese Geige an, die ich auskommentiert habe props: ['value']. Beachten Sie, wie der Anfangswert lautet undefined, anstatt hello: jsfiddle.net/asemahle/8Lrkfxj6 . Nach der ersten Änderung fügt Vue der Komponente dynamisch eine Wertstütze hinzu, damit sie funktioniert.
Asemahle

Ich habe direkt darüber in den Dokumenten gelesen. Tolles Arbeitsbeispiel. +10 wenn ich könnte!
Ton

121

Aus der Dokumentation :

In Vue.js kann die Eltern-Kind-Komponentenbeziehung als Requisiten nach unten und Ereignisse nach oben zusammengefasst werden. Der Elternteil gibt Daten über Requisiten an das Kind weiter, und das Kind sendet Nachrichten über Ereignisse an den Elternteil. Mal sehen, wie sie als nächstes funktionieren.

Geben Sie hier die Bildbeschreibung ein

Wie man Requisiten weitergibt

Es folgt der Code zum Übergeben von Requisiten an ein untergeordnetes Element:

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

So senden Sie ein Ereignis aus

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
Was ist, wenn die Funktion "Inkrement" in der übergeordneten Komponente enthalten war und ich sie über die untergeordnete Komponente auslösen wollte?
Hamzaouiii

der Schermoment des Erfassens des Konzepts, obwohl es mehrmals durch hacky copy paste verwendet wurde ...
Yordan Georgiev

1
Ich werde diese Grafik drucken und sie an meinen Kopf kleben. Vielen Dank!
Domih

1
Sollte in diesem Beispiel kein Ereignis-Listener in der Stammkomponente definiert sein? So etwas wie: `` `mount () {this.on ('increment', () => {this.incrementTotal ();}); } `` `
jmk2142

94

In der untergeordneten Komponente:

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

In der übergeordneten Komponente:

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

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

3
Dieses Beispiel hat mich einfach umgehauen. Sie haben keine Ahnung, wie viele Tutorials ich durchlaufen habe, bevor ich hier
ankam

18

Untergeordnete Komponente

Verwenden Sie this.$emit('event_name')diese Option , um ein Ereignis an die übergeordnete Komponente zu senden.

Geben Sie hier die Bildbeschreibung ein

Übergeordnete Komponente

Um dieses Ereignis in der übergeordneten Komponente abzuhören, führen wir v-on:event_nameeine Methode ( ex. handleChange) aus, die wir für dieses Ereignis ausführen möchten

Geben Sie hier die Bildbeschreibung ein

Getan :)


13

Ich bin mit den Ereignisausgabe- und V-Modell-Antworten für die oben genannten einverstanden. Ich dachte jedoch, ich würde veröffentlichen, was ich über Komponenten mit mehreren Formularelementen gefunden habe, die an ihre übergeordneten Elemente zurückgegeben werden sollen, da dies einer der ersten von Google zurückgegebenen Artikel zu sein scheint.

Ich weiß, dass die Frage eine einzelne Eingabe spezifiziert, aber dies schien die engste Übereinstimmung zu sein und könnte Menschen mit ähnlichen Vue-Komponenten Zeit sparen. Auch hat der .syncModifikator noch niemand erwähnt .

Soweit ich weiß, ist die v-modelLösung nur für eine Eingabe geeignet, die an die übergeordnete Person zurückkehrt. Ich habe ein bisschen Zeit gebraucht, um danach zu suchen, aber die Dokumentation zu Vue (2.3.0) zeigt, wie mehrere in die Komponente gesendete Requisiten wieder an die übergeordnete Komponente synchronisiert werden (natürlich über Emit).

Es wird entsprechend als .syncModifikator bezeichnet.

In der Dokumentation heißt es:

In einigen Fällen benötigen wir möglicherweise eine „Zwei-Wege-Bindung“ für eine Requisite. Leider kann eine echte bidirektionale Bindung zu Wartungsproblemen führen, da untergeordnete Komponenten das übergeordnete Element mutieren können, ohne dass die Quelle dieser Mutation sowohl für das übergeordnete Element als auch für das untergeordnete Element offensichtlich ist.

Aus diesem Grund empfehlen wir stattdessen, Ereignisse im Muster von zu senden update:myPropName. Zum Beispiel titlekönnten wir in einer hypothetischen Komponente mit einer Requisite die Absicht, einen neuen Wert zuzuweisen, kommunizieren mit:

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

Dann kann der Elternteil dieses Ereignis abhören und eine lokale Dateneigenschaft aktualisieren, wenn er möchte. Beispielsweise:

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

Der Einfachheit halber bieten wir eine Abkürzung für dieses Muster mit dem Modifikator .sync an:

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

Sie können auch mehrere gleichzeitig synchronisieren, indem Sie ein Objekt senden. Lesen Sie hier die Dokumentation


Das habe ich gesucht. Vielen Dank.
Thomas

Dies ist die beste und aktuellste Lösung ab 2020. Vielen Dank!
Marcelo

6

Einfacher ist die Verwendung 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>

Mein vollständiges Beispiel: https://codesandbox.io/s/update-parent-property-ufj4b


5

Es ist auch möglich, Requisiten als Objekt oder Array zu übergeben. In diesem Fall werden die Daten in beide Richtungen gebunden:

(Dies wird am Ende des Themas vermerkt: 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) Untergeordnete Komponente: Sie können Folgendes in der untergeordneten Komponente verwenden. Schreiben Sie wie folgt: this.formValue dient zum Senden einiger Daten an die übergeordnete Komponente

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

2) Parrent Compnenet: und im Parrent Component Tag erhalten Sie eine Sendevariable wie folgt: und dies ist der Code, um die untergeordneten Compnent-Daten im Parent Component Tag zu empfangen

@send="newformValue"

0

Eine andere Möglichkeit besteht darin, eine Referenz Ihres Setzers vom Elternteil als Requisite an die untergeordnete Komponente zu übergeben, ähnlich wie sie es in React tun. Angenommen, Sie haben eine Methode updateValueauf dem übergeordneten Element, um den Wert zu aktualisieren. Sie können die untergeordnete Komponente wie folgt instanziieren : <child :updateValue="updateValue"></child>. Dann haben Sie auf dem Kind eine entsprechende Requisite: props: {updateValue: Function}und rufen in der Vorlage die Methode auf, wenn sich die Eingabe ändert : <input @input="updateValue($event.target.value)">.


0

Ich weiß nicht warum, aber ich habe gerade die übergeordneten Daten erfolgreich aktualisiert, indem ich Daten als Objekt verwendet habe. :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

In diesem Beispiel erfahren Sie, wie Sie den Eingabewert über die Schaltfläche "Senden" an das übergeordnete Element übergeben.

Definieren Sie zuerst eventBus als neuen 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

Ich denke, das wird den Trick machen:

@change="$emit(variable)"

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.