Если вы используете vue.js и пытаетесь создать свои собственные компоненты, в какой-то момент вам нужно будет каким-то образом передать некоторую форму данных из дочернего компонента в родительский. Есть много способов добиться одного и того же результата. Некоторые используют внешнее хранилище, некоторые используют объект окна и пользовательские события, а большинство разработчиков используют шину событий.

Vue.js уже предлагает пользовательскую директиву для двусторонней привязки данных. Чтобы включить двустороннюю привязку данных для элемента, все, что нам нужно сделать, это использовать директиву v-model.

Под капотом это не что иное, как простой эмиттер событий, который отправляет данные от дочернего элемента к его родителю с помощью предопределенных событий.

Как работает v-модель?

<input type="text" v-model="message" />

Как мы уже говорили ранее, v-model — не что иное, как прославленный генератор событий. По сути, он связывает свойство value дочернего элемента, чтобы иметь привязку данных между родителем и дочерним элементом. так что-то вроде этого:

<input type="text" v-bind:value="message" />

мы можем использовать :value вместо записи полной директивы v-bind.

для обработки привязки дочерних и родительских данных все, что нам нужно сделать, это создать какое-то событие и сохранить информацию в том же свойстве, которое мы использовали для value :

<input type="text" :value="message" @input=" ... " />

Каждое событие может отправлять с собой некоторые данные. Таким образом, чтобы заполнить пробел « … », нам нужна функция, которая может извлекать эти данные и сохранять их в нашем свойстве message.

мы можем написать простую функцию в methods, чтобы сделать именно это:

data: () => ({
    message: "hello, medium!",
}),
methods: {
  handelInput(val) {
    this.message = val'
  },
},

так что теперь наш исходный ввод будет выглядеть примерно так:

<input type="text" :value="message" @input="handelInput" />

чтобы сделать это короче и красивее, мы можем использовать стрелочные функции ES6 и переписать все это так:

<input type="text" :value="message" @input="val => message = val" />

это в основном то, что делает v-model. Он привязывает value к свойству, прослушивает событие input и устанавливает значение связанного свойства на то, что input выдает ему.

TL;DR

поэтому все, что нам нужно сделать, это добавить свойство value в наш компонент и генерировать событие input всякий раз, когда наше свойство value изменяется.

для этого все, что нам нужно сделать, это что-то вроде этого:

/* ./components/textInput.vue */
<template>
    <input type="text" v-model="msg" />
</template>
<script>
export default {
  props: ["value"],
  data: () => ({
    msg: this.value,
  }),
  watch: {
    msg(newval){
      this.$emit("input", newval);
    },
  },
};
</script>

и в родительских компонентах мы можем написать что-то вроде этого:

/* ./views/homeView.vue */
<template>
    <p> {{ message }} </p>
    <TextInput v-model="message"></TextInput>
</template>
<script>
import TextInput from "../components/textInput.vue";
export default {
  components:{
    TextInput,
  },
  data: () => ({
    message: "hello, medium!",
  }),
};
</script>

Лучший способ добавить v-модель в пользовательские компоненты

Но это не лучший способ реализации v-model для пользовательских компонентов. Потому что структура нашего компонента построена таким образом, что он может легко рассинхронизироваться с родительским компонентом.

Чтобы избежать таких проблем, мы можем использовать ``вычисляемые`` свойства.

Что-то, о чем не многие разработчики vue.js знают или используют, что вычисляемые свойства могут иметь различное поведение в зависимости от того, как вы их обрабатываете. Другими словами, они должны выполнять разные функции, когда вы получаетеот них значение или когда вы устанавливаетедля них значение.

Использование вычисляемых свойств означает, что дочерний компонент не будет иметь состояния и не может быть рассинхронизирован со своим родителем. Единственный способ изменить данные в нашем дочернем компоненте — это изменить его родительский компонент.

computed: {
  message: {
    get() {
      return this.value;
    },
    set(setvalue) {
      this.$emit("input", setvalue);
    },
  },
},

В этом фрагменте кода сообщение возвращает значение, когда вы пытаетесь получить его значение, и выдает ввод событие с новым значением при попытке изменить его значение. Это приведет к тому, что его родитель обновит состояние и изменит значение связанного свойства.

Таким образом, весь наш компонент будет выглядеть так:

/* ./components/textInput.vue */
<template>
<input type="text" v-model="msg" />
</template>
<script>
export default {
  props: ["value"],
  computed: {
    msg: {
      get() {
        return this.value;
      },
      set(setvalue) {
      this.$emit("input", setvalue);
      },
    },
  },
};
</script>

И наш родительский компонент будет выглядеть точно так же, как и раньше:

/* ./views/homeView.vue */
<template>
<p> {{ message }} </p>
    <TextInput v-model="message"></TextInput>
</template>
<script>
import TextInput from "../components/textInput.vue";
export default {
  components:{
    TextInput,
  },
  data: () => ({
    message: "hello, medium!",
  }),
};
</script>