vuejs aktualizuje dane nadrzędne z komponentu podrzędnego


155

Zaczynam bawić się vuejsami (2.0). Zbudowałem prostą stronę z jednym komponentem. Strona ma jedną instancję Vue z danymi. Na tej stronie zarejestrowałem się i dodałem komponent do html. Komponent ma jeden input[type=text]. Chcę, aby ta wartość odzwierciedlała rodzica (główna instancja Vue).

Jak poprawnie zaktualizować dane nadrzędne komponentu? Podanie związanego rekwizytu od rodzica nie jest dobre i powoduje wyświetlenie niektórych ostrzeżeń na konsoli. Mają coś w swoim doktorze, ale to nie działa.


1
Czy możesz dodać wypróbowany kod, który nie działa.
Saurabh

Odpowiedzi:


181

Dwukierunkowe wiązanie zostało wycofane w Vue 2.0 na korzyść używania architektury bardziej sterowanej zdarzeniami. Ogólnie rzecz biorąc, dziecko nie powinno mutować swoich rekwizytów. Powinien raczej $emitzdarzyć się i pozwolić rodzicowi zareagować na te zdarzenia.

W twoim konkretnym przypadku możesz użyć komponentu niestandardowego z v-model. Jest to specjalna składnia, która pozwala na coś zbliżonego do dwukierunkowego wiązania, ale w rzeczywistości jest skrótem opisanej powyżej architektury sterowanej zdarzeniami. Możesz o tym przeczytać tutaj -> https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events .

Oto prosty przykład:

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>


Dokumenty to stwierdzają

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

jest równa

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

Dlatego też właściwość dziecka musi mieć nazwę value i dlaczego dziecko musi emitować zdarzenie o nazwie input.


pierwsze dzięki za odpowiedź. czy możesz rozwinąć, czy lepiej wskazać dokumenty dotyczące wydarzenia „input”? wydaje się, że jest to wydarzenie wbudowane.
Gal Ziv

Dodałem wyjaśnienie i uczyniłem link do dokumentacji bardziej oczywistym.
asemahle

1
Pominąłem właściwość „wartość” dla komponentu i utworzonej funkcji i nadal działa. czy możesz wyjaśnić, dlaczego go użyłeś?
xetra11

1
Jeśli nie dodasz rekwizytu, będzie to undefinedaż do pierwszej zmiany. Zobacz te skrzypce, na których skomentowałem props: ['value']. Zwróć uwagę, jaka jest wartość początkowa undefined, zamiast hello: jsfiddle.net/asemahle/8Lrkfxj6 . Po pierwszej zmianie Vue dynamicznie dodaje wartość właściwości do komponentu, więc działa.
asemahle

Przeczytałem zaraz po tym w dokumentach. Świetny przykład pracy. +10, gdybym mógł!
glina

121

Z dokumentacji :

W Vue.js relację komponentu rodzic-dziecko można podsumować jako rekwizyty w dół, zdarzenia w górę. Rodzic przekazuje dane dziecku poprzez rekwizyty, a dziecko wysyła wiadomości do rodzica poprzez zdarzenia. Zobaczmy, jak będą dalej działać.

wprowadź opis obrazu tutaj

Jak przekazywać rekwizyty

Poniżej znajduje się kod służący do przekazywania właściwości do elementu podrzędnego:

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

Jak wyemitować wydarzenie

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
co by było, gdyby funkcja „increment” znajdowała się w komponencie nadrzędnym i chciałem ją wyzwolić z komponentu potomnego?
Hamzaouiii

moment ścinania uchwycenia koncepcji, choć używam go kilka razy przez hacky copy paste ...
Yordan Georgiev

1
Wydrukuję tę grafikę i przykleję ją do głowy. Dzięki!
domih

1
Czy w tym przykładzie nie powinniśmy mieć detektora zdarzeń zdefiniowanego w komponencie głównym? Coś w rodzaju: `` mount () {this.on ('inkrement', () => {this.incrementTotal ();}); } ``
jmk2142

94

W komponencie podrzędnym:

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

W komponencie macierzystym:

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

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

3
Mój umysł jest po prostu oszołomiony tym przykładem. Nie masz pojęcia, ile samouczków przeszedłem, zanim tu dotarłem ...
suchislife

18

Komponent podrzędny

Służy this.$emit('event_name')do wysyłania zdarzenia do komponentu nadrzędnego.

wprowadź opis obrazu tutaj

Komponent nadrzędny

Aby wysłuchać tego zdarzenia w komponencie nadrzędnym, wykonujemy v-on:event_namei pojawia się metoda ( ex. handleChange), którą chcemy wykonać na tym zdarzeniu

wprowadź opis obrazu tutaj

Gotowe :)


13

Zgadzam się z emisją zdarzenia i odpowiedziami v-model dla tych powyżej. Pomyślałem jednak, że opublikuję to, co znalazłem o komponentach z wieloma elementami formularza, które chcą wyemitować z powrotem do swojego rodzica, ponieważ wydaje się, że jest to jeden z pierwszych artykułów zwróconych przez Google.

Wiem, że pytanie dotyczy pojedynczego wejścia, ale wydawało się, że jest to najbliższe dopasowanie i może zaoszczędzić ludziom trochę czasu dzięki podobnym komponentom vue. Ponadto nikt jeszcze nie wspomniał o .syncmodyfikatorze.

O ile wiem, v-modelrozwiązanie jest odpowiednie tylko dla jednego wejścia powracającego do rodzica. Poszukiwanie tego zajęło mi trochę czasu, ale dokumentacja Vue (2.3.0) pokazuje, jak zsynchronizować wiele właściwości wysyłanych do komponentu z powrotem do elementu nadrzędnego (oczywiście przez emitowanie).

Właściwie nazywa się to .syncmodyfikatorem.

Oto, co mówi dokumentacja :

W niektórych przypadkach możemy potrzebować „dwukierunkowego wiązania” rekwizytu. Niestety, prawdziwe dwukierunkowe wiązanie może powodować problemy z utrzymaniem, ponieważ komponenty potomne mogą mutować rodzica bez źródła tej mutacji, które jest oczywiste zarówno u rodzica, jak i dziecka.

Dlatego zamiast tego zalecamy emitowanie zdarzeń w formacie update:myPropName. Na przykład w hipotetycznym komponencie z titlerekwizytem moglibyśmy zakomunikować zamiar przypisania nowej wartości za pomocą:

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

Następnie rodzic może nasłuchiwać tego zdarzenia i aktualizować lokalną właściwość danych, jeśli chce. Na przykład:

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

Dla wygody oferujemy skrót dla tego wzorca z modyfikatorem .sync:

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

Możesz także zsynchronizować wiele na raz, wysyłając przez obiekt. Sprawdź dokumentację tutaj


To jest to, czego szukałem. Wielkie dzięki.
Thomas

To najlepsze i najbardziej aktualne rozwiązanie na rok 2020. Wielkie dzięki!
Marcelo

6

Sposób jest prostszy w użyciu this.$emit

Ojciec 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>

Mój pełny przykład: https://codesandbox.io/s/update-parent-property-ufj4b


5

Możliwe jest również przekazanie właściwości jako obiektu lub tablicy. W tym przypadku dane będą wiązane dwukierunkowo:

(Zaznaczono to na końcu tematu: 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) Element podrzędny: możesz użyć tego w komponencie potomnym, pisz w ten sposób: this.formValue służy do wysyłania danych do komponentu nadrzędnego

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

2) Parrent Compnenet: iw tagu komponentu równoległego otrzymuj zmienną wysyłaną w ten sposób: i to jest kod do odbioru danych składowych potomnych w tagu komponentu nadrzędnego

@send="newformValue"

0

Innym sposobem jest przekazanie referencji setera od rodzica jako rekwizytu do komponentu potomnego, podobnie jak robią to w Reakcie. Powiedzmy, że masz sposób updateValuena rodziców, aby zaktualizować wartość, można instancję komponentu dziecko tak: <child :updateValue="updateValue"></child>. Następnie na dziecko będzie mieć odpowiedni rekwizyt: props: {updateValue: Function}i w szablonie wywołać metodę, gdy zmienia wejście: <input @input="updateValue($event.target.value)">.


0

Nie wiem dlaczego, ale właśnie pomyślnie zaktualizowałem dane nadrzędne, używając danych jako obiektu :seticomputed

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

jego przykład pokaże, jak przekazać wartość wejściową do rodzica po naciśnięciu przycisku przesyłania.

Najpierw zdefiniuj eventBus jako nowy 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
    });
}

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.