I have two components: Parent and Child. The Parent has an array of cars
the Child is supposed push objects the cars
array. My problem is that my Child component turns cars
into an object, instead of pushing an object into the cars
array. My Parent component:
<template>
<child v-model="cars"></child>
<ul>
<li v-for="car in cars">
{{ car.model }}
</li>
</ul>
</template>
export default {
data: function() {
return {
cars: []
}
}
}
My Child component:
<template>
<div>
<button type="button" @click="addCar()">Add Car</button>
</div>
</template>
export default {
methods: {
addCar() {
this.$emit("input", { model: "some car model" })
}
}
}
Expected results:
cars
gets updated and becomes [{ model: "some car model"}, { model: "some car model"}, etc...]
Actual results:
cars
becomes an object {model: "some car model"}
Here is my fiddle:
https://jsfiddle.net/t121ufk5/529/
I assume something is wrong with the way i am using v-model
on my child component and/or the way I am emitting is incorrect. Can someone help? Thanks in advance!
Lets discuss, why you not get proper result.Then we discuss other approach to solve this problem.
Firstly we need to understand how v-model
works on custom components by default.
When using a text input
(including types such as email
, number
, etc.) or textarea
, v-model="varName"
is equivalent to :value="varName" @input="e => varName = e.target.value"
. This means that the value of the input is set to varName
after each update to the input varName
is updated to the value of the input. A normal select element will act like this too, though a multiple select will be different.
Now we need to understand,
How Does v-model Work On Components?
Since Vue doesn’t know how your component is supposed to work, or if it’s trying to act as a replacement for a certain type of input, it treats all components the same with regards to v-model
. It actually works the exact same way as it does for text inputs, except that in the event handler, it doesn’t expect an event object to be passed to it, rather it expects the value to be passed straight to it. So…
<my-custom-component v-model="myProperty" />
…is the same thing as…
<my-custom-component :value="myProperty" @input="val => myProperty = val" />
So when you apply this approach. You have to receive value
as a props. and make sure you $emit
name is input
.
Now you can ask me at this stage,what you do wrong?
Ok, look at like code @input="val => myProperty = val"
when you $emit
whit a new value. this newValue
will updated our parent value which you wanna update.
Here is your code this.$emit("input", { model: "some car model" })
.
You update your parent value with a object. So your Array
updated with a Object
.
Lets solve the full problem.
Parent Component: `
<template>
<child v-model="cars"></child>
<ul>
<li v-for="car in cars">
{{ car.model }}
</li>
</ul>
</template>
export default {
data: function() {
return {
cars: []
}
}
}
`
Child Component:
<template>
<div>
<button type="button" @click="addCar()">Add Car</button>
</div>
</template>
export default {
props: ['value']
methods: {
addCar() {
this.$emit("input", this.value.concat({model: "some car model"}))
}
}
}
You can actually solved it several way.
Second Approach,
Parent:
<template>
<child :cars="cars"></child>
<ul>
<li v-for="car in cars">
{{ car.model }}
</li>
</ul>
</template>
export default {
data: function() {
return {
cars: []
}
}
}
Child:
<template>
<div>
<button type="button" @click="addCar">Add Car</button>
</div>
</template>
export default {
props: {
cars: {
type: Array,
default:[]
}
},
methods: {
addCar() {
this.cars.push({ model: "some car model" })
}
}
}
Last Approach:
Parent:
<template>
<child @update="addCar"></child>
<ul>
<li v-for="car in cars">
{{ car.model }}
</li>
</ul>
</template>
export default {
data() {
return {
cars: []
}
}
},
methods: {
addCar() {
this.cars.push({ model: "some car model" })
}
}
}
Child:
<template>
<div>
<button type="button" @click="update">Add Car</button>
</div>
</template>
export default {
methods: {
update() {
this.$emit('update')
}
}
}