Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue.js - emit to update array not working

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!

like image 762
John Grayson Avatar asked Aug 05 '18 13:08

John Grayson


2 Answers

It is possible to trigger the event of updating the transmitted value in props

In the parent

<my-component :is-open.sync="isOpen" />

In my-component

this.$emit('update:isOpen', true)
like image 78
Pavel Levin Avatar answered Sep 30 '22 02:09

Pavel Levin


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')
            }
        }
    }
like image 21
HB Shifat Avatar answered Sep 30 '22 02:09

HB Shifat