d s
d s

Reputation: 171

How do I get v-model data from a child component Vue 2?

I made a custom input component and want to get data from it in the parent component. At first I used the component as it was written in the guide:

Input.vue

<input
      :value="value"
      @input="$emit('input', $event.target.value)"
      class="w-full border-[1px] bg-transparent p-4 lg:text-sm placeholder:text-[#97999B]"
      :placeholder="placeholder"
      :class="statusStyles"
    />

MyComponent.vue

    <Input
          placeholder="Phone number"
          type="text"
          v-model="phone"
        />

Everything worked, but I broke this code into components and another wrapper appeared, it looks like this:

Form.vue

<OrderFormInfo
      v-if="step === 'info'"
      :name="name"
      :apart="apart"
      :city="city"
      :phone="phone"
      :postal="postal"
      :region="region"
      :address="address"
      @next-step="handleNext"
    />

OrderInfo.vue

        <Input
          placeholder="phone number"
          type="text"
          v-model="phone"
        />
        <Input
          placeholder="recipient name"
          type="text"  
          v-model="name"
        />

Input.vue

<template>
  <div class="w-full space-y-[10px]">
    <input
      :value="value"
      @input="$emit('input', $event.target.value)"
      class="w-full border-[1px] bg-transparent p-4 lg:text-sm placeholder:text-[#97999B]"
      :placeholder="placeholder"
      :class="statusStyles"
    />
    <p v-if="errorStatus" class="text-red-500">{{ errors[0] }}</p>
  </div>
</template>

<script>
export default {
  props: {
    errors: Array,
    sucess: Boolean,
    value: String,
    errorStatus: Boolean,
    placeholder: String,
  },
  computed: {
    statusStyles() {
      if (this.errorStatus) {
        return "border-red-500 text-red-500";
      }
      if (!this.errorStatus && this.value.length > 3) {
        return "bg-white border-black text-black";
      }
      return "text-black  border-[#97999B]";
    },
  },
};
</script>

How do I get the data from OrderInfo.vue in Form.vue? I've tried passing data through props, but the vue gives an error that you can't do that. I don't understand how to use v-model with nested components

Upvotes: 1

Views: 976

Answers (3)

Neha Soni
Neha Soni

Reputation: 4684

There are a few ways to do this. For example-

  1. Using Event Bus (to emit event when data updates.)
  2. Using Vuex (to update in state and access it anywhere)

The above two solutions required some installation. The easiest way would be to use this.$root.$emit to emit an event to any component without using any global variable or bus or props.

Here is a working demo. Try changing the phone and name values from the input field (OrderFormInfo.vue), and you should see the changes in Form.vue (parent component).

Vue.component('orderFormInfo', {
  props: ['prop_name', 'prop_phone'],
  data() {
    return {
      name: this.prop_name,
      phone: this.prop_phone,
    }
  },
  watch: {
    name(newVal, oldVal) {
      this.$root.$emit('onNameUpdate', newVal);
    },
    phone(newVal, oldVal) {
      this.$root.$emit('onPhoneUpdate', newVal);
    }
  },
  template: `<div>
    <b>OrderFormInfo.vue</b><br>
    <input-field
      placeholder="phone number"
      type="text"
      v-model="phone"/>
    <input-field
      placeholder="recipient name"
      type="text"
      v-model="name"/>
  </div>`
});

Vue.component('inputField', {
  props: ['value', 'placeholder'],
  template: `<input
:value="value"
@input="$emit('input', $event.target.value)"
:placeholder="placeholder"
/>`
});

var app = new Vue({
  el: '#app',
  data() {
    return {
      name: 'My Name',
      phone: '9090909090'
    }
  },
  created() { 
    this.$root.$on('onNameUpdate', (name) => {
      this.name = name;
    });
    this.$root.$on('onPhoneUpdate', (phone) => {
      this.phone = phone;
    })
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <b>Form.vue</b><br>
  Name - {{ name }} <br>
  Phone - {{ phone }} <br><br>
  <order-form-info :prop_name="name" :prop_phone="phone"></order-form-info>
  
</div>

Important-
When you pass props from Form.vue to OrderFormInfo.vue, do not try to mutate that prop directly. First, copy the props to the data variable and mutate those. Read the reason here.

Upvotes: 0

d s
d s

Reputation: 171

I was able to solve it this way, I found an article on the Internet. Here are the changes:

OrderFormInfo.vue

     <Input
          placeholder="Phone number"
          type="text"
          :error-status="false"
          :errors="[]"
          :value="phone"
          @input="$emit('input-phone', $event)"
     />

OrderForm.vue

  <OrderFormInfo.
      v-if="step === 'info'"
      :name="name"
      @input-name="name = $event"
      :phone="phone"
      @input-phone="phone = $event"
      @next-step="handleNext"
  />

Upvotes: 0

Roh&#236;t J&#237;ndal
Roh&#236;t J&#237;ndal

Reputation: 27192

You can watch the props by using a watcher function in your parent (Form.vue) component inside the mounted hook.

You have to just attach a ref to your top most child component. For ex :

<order-form-info :name="name" :phone="phone" ref="orderFormComponent"></order-form-info>

Live Demo :

Vue.component('orderFormInfo', {
  props: ['name', 'phone'],
  template: `<div>
    <input-field
      placeholder="phone number"
      type="text"
      v-model="phone"/>
    <input-field
      placeholder="recipient name"
      type="text"
      v-model="name"/>
  </div>`
});

Vue.component('inputField', {
  props: ['value', 'placeholder'],
  template: `<input
:value="value"
@input="$emit('input', $event.target.value)"
:placeholder="placeholder"
/>`
});

var app = new Vue({
  el: '#form',
  data: {
    name: 'Alpha',
    phone: '1111111111'
  },
  mounted() {
    this.$watch(
      "$refs.orderFormComponent.phone", (newVal, oldVal) => {
         console.log(newVal, oldVal)
      }
    );
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="form">
  <order-form-info :name="name" :phone="phone" ref="orderFormComponent"></order-form-info>
</div>

Upvotes: 2

Related Questions