Search code examples
javascriptvue.jssortablejsvuedraggablevue.draggable

Vue draggable does not update text input fields when switched, but does update it in the array


I am trying to implement a drag and drop document builder with Vue draggable, you can add elements like headings and rearrange their order. These involve using components with input fields in.

I have got it working ok, except when you type something into heading 1 for example, then swap it with heading 2, the input text you typed is still in the same position where heading 1 was, even though the element swapped. But bizarely, it does switch it round on the array list correctly.

Basically, the input you type doesn't seem to stay with the component when you swap it. It either stays in its ORIGINAL place or just clears, which is obviously very problematic.

It uses a dynamic component looping through the array to display the list, it also involves emitting an object which then updates in the array:

AddNew.vue

<InsertContent @create="createNewElement($event, 0)" />
        <draggable :list="userData.packetSections" :options="{animation:750}" handle=".handle">
          <div
            class="element-wrapper"
            v-for="(section, index) in userData.packetSections"
            :key="index"
          >
            <div class="element">
              <component :is="section.type + 'Element'" @return="addData($event, index)" />
              <i class="remove" @click="removeElement(index)"></i>
            </div>
            <InsertContent @create="createNewElement($event, index + 1)" />
          </div>
        </draggable>

<script>
  data() {
    return {
      userData: {
        packetSections: [
          {
            type: "Heading",
            text: "test input", //<-- this swaps in the array when switched but does NOT swap in the browser
            id: ""
          },
          {
            type: "Heading",
            text: "test 2",
            id: ""
          },
        ],
      }
    };
  },
  methods: {
    createNewElement(event, index) {
      var element = {
        type: event,
        text: "",
        id: ""
      };
      this.userData.packetSections.splice(index, 0, element);
    },
    removeElement(index) {
      this.userData.packetSections.splice(index, 1);
    },
    addData(emittedData, index) {
      this.userData.packetSections.splice(index, 1, emittedData);
      console.log(emittedData, index);
    },
  }
};
</script>

HeadingElement.vue

<template>
  <div class="grey-box">
    <h3>Subheading</h3>
    <input type="text" v-model="data.text" @change="emitData" />
  </div>
</template>

<script>
export default {
  data(){
    return{
      data: {
        type: "Heading",
        text: "",
        id: ""
      }
    }
  },
  methods:{
    emitData(){
      this.$emit('return', this.data);
    }
  }
};
</script>

Does anyone know why the text input fields would update in the array but NOT update the position in the browser?

Thanks


Solution

  • For anyone interested, I ended up fixing this. you have to pass down the contents of the array as props when you are working with dynamic components in your list that also pass up data.

    So I change it from: <component :is="section.type + 'Element'" @return="addData($event, index)" />

    to: <component :is="section.type + 'Element'" :data="userData.packetSections[index]" @return="addData($event, index)" />

    and it worked fine.