Dynamically add an element to a sortable LI in Vue

Viewed 225

I have a sortable list where I am trying to accomplish two things:

  1. Add/Remove LI elements to this list with a +/- symbol.
  2. The sortable list has a textarea, with data - when I add an remove LI items, I am having difficulty storing this in the data property. When I reload the page, I want for instance my new LI items to be visible with the new data items.

Any direction much appreciated.

HTML

<div id="app" class="container ">
  <ul ref="list" class="list" v-sortable="{animation: 250, onUpdate: work}">
    <li v-for="item in orderedItems" :_id="item.id" :order="item.order">
      |||
      <textarea row="5" v-model="item.text"></textarea>
    </li>
  </ul>
  <pre>{{ $data }}
</div>

JS

Vue.directive('sortable', {
  // When the bound element is inserted into the DOM...
  inserted: function (el, binding) {
    Sortable.create(el, binding.value || {}) 
  }
})

new Vue({
  el: '#app',
  
  data: {
    items: [
      {id: 1, text: 'AAA', order: 2},
      {id: 2, text: 'BBB', order: 5},
      {id: 3, text: 'CCC', order: 1},
      {id: 4, text: 'DDD', order: 4},
      {id: 5, text: 'EEE', order: 3},
    ]
  },
  
  created: function()
  {
    this.orderedItems = _.orderBy(this.items, 'order')
  },
  
  methods: {
    work: function(event)
    {
      var vm = this
      var els = vm.$refs.list.children
      
      for (var i=0; i < els.length; i++) 
      {
        var id = els[i].getAttribute('_id')        
        vm.items.map(function(item)
                     {
          if (item.id == id)
          {
            item.order = i+1
          }
        })
        //console.log(vm.items[i].order)  
      }
    }
  }
})

Here is the sortable list: https://codepen.io/syedhuss/pen/bGBZoEe

2 Answers

Your code looks fine to me, it's a conceptual problem what you have related to persistence.

Vue by itself does not store anything you tweak in the client, so it's completely normal that you refresh your page and things go back to default. You should store the new items you add in a database or use a mechanism like web storage, cookies etc to store data in the client side.

I think you are requiring a database here, where you save all the items added by a user

There are a few issues:

  1. orderedItems needs to be declared in data() for it to be reactive.

    export default {
      data() {
        orderedItems: []
      }
    }
    
  2. work() updates the DOM rather than the bound model (orderedItems in this case). But orderedItems needs to be reactive for Vue to render the changes.

  3. It looks like work() is intended to increment the order property of each item, but I suspect you actually want to change the item order based on the moved item. The event argument contains oldIndex and newIndex, which refers to the array index of the moved item within orderedItems. You could use that info with Array.prototype.splice to perform the move in orderedItems:

    const movedItem = this.orderedItems.splice(oldIndex, 1) // remove
    this.orderedItems.splice(newIndex, 0, movedItem) // insert
    
  4. Given that the items in v-for are being reordered or removed, it's important to specify a unique key for each item so that Vue renders the items properly. In this case, item.id is sufficiently unique between items, so that should be bound to key:

    <li v-for="item in orderedItems" :key="item.id"></li>
    

updated codepen

Related