vuejs обновляет родительские данные из дочернего компонента


155

Я начинаю играть с vuejs (2.0). Я построил простую страницу с одним компонентом. На странице есть один экземпляр Vue с данными. На этой странице я зарегистрировался и добавил компонент в HTML. Компонент имеет один input[type=text]. Я хочу, чтобы это значение отражалось на родителе (основной экземпляр Vue).

Как правильно обновить родительские данные компонента? Передача привязанного реквизита от родителя не годится и выдает некоторые предупреждения на консоль. У них есть что-то в их документе, но это не работает.


1
Можете ли вы добавить код, который вы пробовали, который не работает.
Саураб

Ответы:


181

Двусторонняя привязка в Vue 2.0 устарела в пользу использования более управляемой событиями архитектуры. В общем, ребенок не должен мутировать свои реквизиты. Скорее это должно $emitсобытия и позволить родителю реагировать на эти события.

В вашем конкретном случае вы можете использовать пользовательский компонент с v-model. Это специальный синтаксис, который допускает нечто близкое к двустороннему связыванию, но на самом деле это сокращение для управляемой событиями архитектуры, описанной выше. Вы можете прочитать об этом здесь -> https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events .

Вот простой пример:

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>


Документы утверждают, что

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

эквивалентно

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

Вот почему опору для ребенка нужно назвать по значению, и почему ребенок должен $ эмитировать событие с именем input.


первое спасибо за ответ. Можете ли вы расширить или лучше указать документы о событии ввода? это похоже на встроенное событие.
Гал Зив

Я добавил пояснения и сделал ссылку на документацию более очевидной.
asemahle

1
Я опустил опору "value" для компонента и созданной функции, и он все еще работает. Вы можете объяснить, почему вы использовали это?
xetra11

1
Если вы не добавите опору, то это произойдет undefinedдо первого изменения. Смотрите эту скрипку, где я закомментировал props: ['value']. Обратите внимание на начальное значение undefinedвместо hello: jsfiddle.net/asemahle/8Lrkfxj6 . После первого изменения Vue динамически добавляет значение prop к компоненту, чтобы оно работало.
Asemahle

Я прочитал это в документации. Отличный рабочий пример. +10 если бы я мог!
глина

121

Из документации :

В Vue.js отношения родительских и дочерних компонентов могут быть обобщены как подпорки, события вверх. Родитель передает данные потомку через реквизиты, а потомок отправляет сообщения родителю через события. Давайте посмотрим, как они работают дальше.

введите описание изображения здесь

Как передать реквизит

Ниже приведен код для передачи реквизитов дочернему элементу:

<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
    }
  }
})

5
Что делать, если функция «Инкремент» была в родительском компоненте, и я хотел вызвать ее из дочернего компонента?
Hamzaouiii

Момент сдвига в понимании концепции, хотя несколько раз ее использовали хакерская копировальная паста ...
Йордан Георгиев

1
Я распечатаю этот рисунок и приклею его к своей голове. Спасибо!
Домих

1
В этом примере не должен ли мы иметь прослушиватель событий, определенный в корневом компоненте? Примерно так: `` `mount () {this.on ('increment', () => {this.incrementTotal ();}); } `` `
jmk2142

94

В дочернем компоненте:

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

В родительском компоненте:

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

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

3
Мой разум просто взорван этим примером. Вы не представляете, сколько учебников я прошел, прежде чем попал сюда ...
suchislife

18

Дочерний компонент

Используйте this.$emit('event_name')для отправки события в родительский компонент.

введите описание изображения здесь

Родительский компонент

Для того чтобы прослушать это событие в родительском компоненте, мы делаем v-on:event_nameи метод ( ex. handleChange), который мы хотим выполнить для этого события, происходит

введите описание изображения здесь

Готово :)


13

Я согласен с ответами о событиях и v-model для вышеупомянутых. Однако я подумал, что опубликую то, что я нашел, о компонентах с несколькими элементами формы, которые хотят отправить обратно своим родителям, так как эта статья кажется одной из первых статей, возвращаемых Google.

Я знаю, что в вопросе указывается один вход, но это кажется самым близким совпадением и может сэкономить людям некоторое время с подобными компонентами vue. Кроме того, никто не упомянул.sync модификатор.

Насколько я знаю, v-model решение подходит только для одного ввода, возвращающего их родителю. Я потратил немного времени на его поиск, но документация Vue (2.3.0) действительно показывает, как синхронизировать несколько реквизитов, отправляемых в компонент, к родителю (конечно, через emit).

Это соответственно называется .sync модификатором.

Вот что говорится в документации :

В некоторых случаях нам может понадобиться «двусторонняя привязка» для опоры. К сожалению, истинное двустороннее связывание может создать проблемы обслуживания, потому что дочерние компоненты могут видоизменить родительский объект, при этом источник этой мутации не будет очевиден как в родительском, так и в дочернем элементах.

Поэтому вместо этого мы рекомендуем генерировать события по схеме update:myPropName. Например, в гипотетическом компоненте с titleпропеллером мы могли бы сообщить о намерении присвоить новое значение с помощью:

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

Затем родитель может прослушать это событие и обновить свойство локальных данных, если он этого хочет. Например:

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

Для удобства мы предлагаем сокращение для этого шаблона с модификатором .sync:

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

Вы также можете синхронизировать несколько одновременно, посылая через объект. Проверьте документацию здесь


Это то, что я искал. Большое спасибо.
Томас

Это лучшее и самое современное решение на 2020 год. Большое спасибо!
Марсело

6

Более простой способ использования 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>

Мой полный пример: https://codesandbox.io/s/update-parent-property-ufj4b


5

Также возможно передавать реквизиты как объект или массив. В этом случае данные будут двусторонне связаны:

(Это отмечено в конце темы: 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) Дочерний Compnent: вы можете использовать как это в дочернем компоненте, напишите так: this.formValue для отправки некоторых данных в родительский компонент

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

2) Parrent Compnenet: и в теге parrent-компонента получают переменную send, например, вот так: и это код для получения данных дочернего компонента в теге родительского компонента.

@send="newformValue"

0

Другой способ - передать ссылку вашего установщика от родителя в качестве реквизита на дочерний компонент, аналогично тому, как они это делают в React. Скажем, у вас есть метод updateValueна родителе для обновления значения, можно создать экземпляр компонента ребенка следующим образом: <child :updateValue="updateValue"></child>. Тогда на ребенке вы будете иметь соответствующую опору: props: {updateValue: Function}и в шаблоне вызова метода при изменении входного: <input @input="updateValue($event.target.value)">.


0

Я не знаю почему, но я только что успешно обновил родительские данные, используя данные в качестве объекта, :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

его пример расскажет вам, как передать входное значение родителю при нажатии кнопки «Отправить».

Сначала определите eventBus как новое 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
    });
}

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.