How can I refresh the positions of all items in a list using Vue-Draggable and Acts As List?

Working on my project, I have integrated a Rails API backend with the acts_as_list gem and a Vue frontend utilizing the Vue Draggable package.

The drag and drop functionality is functioning as expected, with a PUT request being sent to the server. However, there are some strange occurrences that I've noticed:

Scenario 1: When I drag an item from position 1 to position 2, expecting them to switch places, the PUT request is sent but the actual update does not occur.

Scenario 2: If I move an item from position 1 to position 3, causing positions 2 and 3 to shift accordingly, sometimes I see the update reflected on the server but not consistently.

I am seeking a solution where the entire list gets updated upon dragging an item.

TodoList.vue

<template>
    <div class="todos-container" v-if='trips.loaded'>
      <draggable 
        :list='todoList' 
        :options='{animation:150}'
        tag='ul' 
        class='list-group' 
        @change='changed(todoList, $event)'
      >
        <transition-group type='transition'>
          <li v-for='(todo, index) in todoList' :key='todo.id' class='list-group-item'>
            <v-icon class='drag-handle'>drag_handle</v-icon>
            <v-checkbox
              v-model="todoList[index].completed"
              :ripple='false'
              :label='`${todo.title}`'
              color='primary'
              @change='handleTodoClick(todo, index)'
            />
            <v-icon class='remove-todo' @click='handleTodoDelete(todo, index)'>close</v-icon>
          </li>

        </transition-group>
      </draggable>
    </div>
  </todo-list-styles>
</template>

<script>
  import { mapActions } from 'vuex';
  import draggable from 'vuedraggable';

  export default {
    props: {
      trips: {
        type    : Object,
      },
      index: {
        type    : Number,
        required: true,
      }
    },
    computed: {
      todoList() {
        return this.trips.data[this.index].todos;
      }
    },
    methods: {
      ...mapActions('trips', [
        'updateTodoPosition'
      ]),
      handleTodoClick: function(todo, index) {
        console.log('checked')
      },
      handleTodoDelete: function(todo, index) {
        console.log('clicked');
      },
      changed: function(todoList, $event) {
        const {oldIndex, newIndex} = $event.moved;
        const todo = todoList[newIndex];
        const payload = {
          oldIndex,
          newIndex,
          todo,
        };
        this.updateTodoPosition(payload);
      },
    },
    components: {
      draggable,
    },
  }
</script>

Params

Started PUT "/2/update_todo_position" for 127.0.0.1 at 2019-05-13 08:46:09 -0500
Processing by V1::TripsController#update_todo_position as */*
  Parameters: {"oldIndex"=>0, "newIndex"=>2, "todo"=>{"id"=>2, "title"=>"Book Car Rental", "completed"=>true, "position"=>2}, "todo_id"=>"2", "trip"=>{"oldIndex"=>0, "newIndex"=>2, "todo"=>{"id"=>2, "title"=>"Book Car Rental", "completed"=>true, "position"=>2}}}
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  TodoItem Load (0.2ms)  SELECT  "todo_items".* FROM "todo_items" WHERE "todo_items"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
   (0.2ms)  BEGIN
  CACHE User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  UpcomingHike Load (0.3ms)  SELECT  "upcoming_hikes".* FROM "upcoming_hikes" WHERE "upcoming_hikes"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
   (0.2ms)  COMMIT
Completed 200 OK in 5ms (ActiveRecord: 1.1ms)

trips_controller.rb

module V1
  class TripsController < ApplicationController

    ...

    def update_todo_position
      # TODO: Figure out why position being saved is incorrect
      todo = TodoItem.find(params[:todo][:id])
      todo.update!(position: params[:newIndex])
      head :ok
    end

    ...

  end
end

Answer №1

Despite taking a different approach that may not be considered ideal, I've managed to make it work. Essentially, I'm sending the entire updated list from my frontend to my controller, where each item's position is individually updated. While this method deviates from the recommended VueDraggable approach and may not be the most efficient way of handling it in the controller:

Vue component method:

changed: function(todoList, $event) {
        const {newIndex} = $event.moved;
        const todo = todoList[newIndex];
        const newListOrder = JSON.stringify(this.todoList.map(todo => todo.id));
        const payload = {
          todo,
          newListOrder,
        }
        this.updateTodoPosition(payload);
      },

Controller:

def update_todo_position
      newListIds = params[:_json]
      newListIds.each.with_index(1) do |todo, index|
        todo = TodoItem.find(todo)
        todo.update(position: (index))
      end
      head :ok
    end

If you have any suggestions or recommendations, please feel free to share!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Input field with JQuery datepicker showing only months and years

I have encountered a question that closely resembles the one discussed here: year/month only datepicker inline The scenario I'm facing involves utilizing the input version instead of the div. In the case of using the div, the ui-datepicker-calendar ...

What could be causing the issue of undefined component props in Vue Router?

Recently diving into the world of Vue, I've been eager to master the art of utilizing Vue router. Smooth sailing with normal routing, I decided to spice things up by experimenting with dynamic routing. To my surprise, everything ran smoothly until I a ...

What is the best way to distinguish between node modules and async chunks in webpack configuration?

I have a webpack configuration where the vendor chunk is separated from the main bundle. However, I am also using lazy loading on routes and I need to separate the rest of the node modules from those lazy loading chunks as well. I attempted to do this with ...

Create a new array containing two objects that have values taken from two separate objects with the same key in Javascript

I've been attempting to achieve something I've been wanting to do: I have two objects with the same keyName but different values. I need to create a new array that contains a new object with two entries, each holding the values from the two obje ...

Acquiring a new access token through oauth2 for Google Contacts API v3.0

I am utilizing oauth2 in my project with node.js and the connect-oauth library to establish a connection to the latest version (3.0) of the Google Contacts API. Upon authentication, I receive a response that includes: { access_token : "...", &qu ...

The issue arises when a continuous use of angularjs directives linked with an external template fails to display correctly upon the addition of new

In the code snippet below, you'll find a fiddle that displays 3 columns of numbers that increase in value. These columns represent directives with different templates: one inline, one preloaded, and one from an external template. When you click on the ...

How can I use Ajax code to send data to a PHP page and receive the results as a JSON-encoded multidimensional array containing information on each item?

Apologies for the unconventional question title. What I am trying to accomplish is managing two tables in my database: a hotel table (table1) and a room type table (table2). My goal is to allow customers to customize their travel packages by changing hote ...

Why is it that consolidating all my jQuery plugins into one file is ineffective?

Prior to this, I included the following scripts: <script type="text/javascript" src="{{MEDIA_URL}}js/plugins/json2.js"></script> <script type="text/javascript" src="{{MEDIA_URL}}js/plugins/jquery-msdropdown/js/jquery.dd.js"></script&g ...

An error is triggered by serializing a TinyBox POST form into an Array

When I implemented the code below, it performed as anticipated: TINY.box.show({url:target, post:$("form[name='currentSearch']").serialize(), width:650, mask:true, close:true, maskid:'boxMask', boxid:'popupBox', openjs:funct ...

Error in Typescript for a function that accepts several types of lists - property does not exist on the interface

In my project, I have defined three interfaces: a base interface and two others that extend the base one with children as properties. One of the interfaces includes 'valueType' while the other includes 'returnType'. Here is how they are ...

The initial display of jqGrid columnChooser may not show the expected effect on the first attempt

Trying to implement jqGrid in my project has been mostly successful, but I am encountering two specific issues: The first time the columnChooser is clicked, it does not display the effect when clicking the done button. However, it works as intended on th ...

Facing an error when refreshing a page in NextJS while using p5.js

For my website's about page, I'm incorporating the react-p5 library to create a PerlinNoise wave animation. However, I have noticed a strange issue - when I include the p5 component on any page, it redirects me to a 404 error page. But if I navig ...

Dealing with the "TypeError: Cannot read property 'style' of undefined" error in material-ui Transitions: A troubleshooting guide

While attempting to incorporate transitions within my react app, I encountered a recurring error whenever I tried to implement any transition module: "TypeError: Cannot read property 'style' of undefined". (anonymous function) node_modules/ ...

Binding attributes in knockoutjs following an AJAX request

The objective Incorporate the attr binding of KnockoutJS following an AJAX call. The dilemma Check out the code snippet below: $.ajax({ url: "/Products/List?Output=JSON", dataType: "json", success: function (data) { $.each(data, fun ...

Transitioning from Vue2 to Vue3

I have been working on migrating from Vue2 to Vue3. I updated most of the packages to their latest versions and removed some redundant ones to streamline the process. Here is an overview of my package.json: { "private": true, "script ...

Is there a way to retrieve the value of a particular attribute while hovering the mouse over it?

When I hover my mouse over the innerHTML content, certain words are highlighted with a title attribute value. How can I retrieve the specific title value of the content I am hovering over? This should be done using the mouseover event in the TypeScript fil ...

Is it possible to add values to an array of objects only if a certain condition is met

I am dealing with an array of objects structured like this [ { "monthlyData": [ { "dateYear": "2020-07", "data": [ { "id": "45bf4792-c5a5-44ed-b7e8-575 ...

Exploring a JavaScript file with the power of JavaScript and HTML

I have a .js file that contains the following data (excerpt for brevity) var albums= "tracks":[ {"title":"Dunnock","mp3":"Birdsong-Dunnock.mp3", "lyrics":"The Dunnock or hedge sparrow has a fast warbling song often delivered from t ...

Manipulating strings within strings with JavaScript

It's been a strange discovery I made while working with JavaScript. Whenever I try to assign a two-word string to a CSS property, like setting the fontFamily property of an HTML element to "Arial Black", it ends up looking something like thi ...

Although Sequelize has the capability to generate tables, it lacks the ability to perform insertions or selections of data

Currently, I am in the process of developing an express app that incorporates basic MVC functionality with Sequelize. At this stage, my focus is on creating a route to insert a single row of student data into the database. Upon starting the server, Sequel ...