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

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

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

Ответ 1

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

В вашем конкретном случае вы можете использовать пользовательский компонент с 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>

Ответ 2

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

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

enter image description here

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

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

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

Ответ 3

В дочернем компоненте: this.$emit('eventname', this.variable)

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

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

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

Ответ 4

Я согласен с ответами о событиях и V-модели для тех, кто выше. Тем не менее, я подумал, что опубликую то, что я нашел, о компонентах с несколькими элементами формы, которые хотят отправить обратно своим родителям, так как это кажется одной из первых статей, возвращаемых 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>

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

Ответ 5

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

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

enter image description here

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

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

enter image description here

Готово :)

Ответ 6

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

(Это отмечено в конце темы: 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>

Ответ 7

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