Reputation: 171
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
Reputation: 4684
There are a few ways to do this. For example-
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
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
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