Ask A Question

Notifications

You’re not receiving notifications from this thread.

Sortable Drag and Drop Discussion

Discussion for Sortable Drag and Drop

Just a heads up, in your Gemfile, bootstrap 4 beta no longer uses tether for tooltips, now uses popper.js which is now a dependency of the bootstrap 4 gem

Reply

Yeah! Popper is nice to have instead. Screencast was using an old version of my Rails template which has been updated already, just hadn't done a git pull recently on that machine. Should update that in the repo.

Reply

I'd love to see you go further into this topic with Vuejs!

Reply
Jacqueline Chenault Jacqueline Chenault

I'd DEFINITELY be interested in the Vuejs version - I've got an app in mind that it would be very helpful for...

Reply

This approach for updating reordered records will produce O(n) queries. If you use Postgres, it is possible to do the same operation with a single update: https://stackoverflow.com/a... The only disadvantage here is that ActiveRecord can't do queries like this (correct me if I'm wrong), so it will take direct SQL execution.

Reply

+1 for VueJS as well

Reply

Here's another implementation that avoids excess queries and lets you use the standard update controller action. It uses the insert_at method that acts_as_list provides to move the item within the list:

https://gist.github.com/dva...

# app/models/question.rb
def new_position=(position)
insert_at(position.to_i)
end

Make sure to add :new_position to permitted params in questions controller

# coffeescript
$(document).on 'turbolinks:load', ->
$('.sortable').sortable
axis: 'y'
handle: '.handle'
update: (e, ui) ->
data = new FormData
data.append "question[new_position]", ui.item.index() + 1
Rails.ajax
url: "/questions/#{ui.item.data('question-id')}"
type: "PATCH"
data: data

Reply
Thank you this was an awesome tutorial, will definitely be watching more from you!! 
Reply
A few pretty basic observations/questions:

(1) When I use: 
Rails.ajax
nothing hits the server after the drag/drop is complete. 

I changed to:
$.ajax 
and that fixed the problem. Any ideas why the former did not work and the latter did?

(2) Instead of 
//= require jquery-ui/widget
//= require jquery-ui/sortable

I needed to use
//= require jquery-ui/widgets/sortable
to avoid an error message. I suspect this has something to do with either (a) a new version of jquery-ui or (b) my lack of understanding of the asset pipeline.

(3) My implementation required me to the skip authenticity token on the sort method with:
skip_before_action :verify_authenticity_token, only: [:sort]

How does the code in this tutorial work without skipping the authenticity token verification?

Thanks, as always, for such wonderful tutorials!

Reply
Thanks Joel!

Some answers to your questions (not exactly in order):

1. Rails.ajax is the new replacement for jquery's ajax ($.ajax) now that Rails no longer comes with jQuery. Rails.ajax is also smart enough to include the authenticity token in every request which is why you don't have to do #3. 

3. For your code, you should include the authenticity token in the request instead of disabling the check as you'll open yourself up to security vulnerabilities by turning it off.

2. jQuery-ui may have moved sortable to the widgets folder since I recorded the episode. Things are always changing so you'll often run into little changes like that.
Reply
Thanks, Chris. Strangely, Rails.ajax doesn't fire an ajax request for me even though $.ajax works fine. I'm using Rails 5.1.5 and I've confirmed the rails-ujs file is included. Good to know about the authenticity token being included in every request with Rails.ajax.
Reply
hey guys thank you both, Joel your comment helped me to solve my problem. And Chris, man I love what you're doing, I started to learn Ruby and Rails some months ago and you already helped me a lot with your tutorials. What you're doing is something that money cant pay honestly. Keep going 
Reply

The same was happening to me when using Rails.ajax(), I found this online and now it works:
$("#sections").sortable({
update: function(e, ui) {
Rails.ajax({
dataType: 'script',
url: $(this).data("url"),
type: "PATCH",
beforeSend: function() {
return true
},
data: $(this).sortable('serialize'),
});
}
});

Reply
Would love to see a Trello clone!

Reply
Hey Seph! I did a whole series on it here: https://gorails.com/series/vuejs-trello-clone-in-rails

Hope you enjoy it!
Reply
Hi Chris, I would need to be able to sort items in a list as you've shown in this episode, but also move items between linked lists. How would you implement this? Problem is that in this case I need to remove the item from the old list and add it to the new list, besides updating the positions, and the new positions should be correct in both lists without leaving gaps in any of them. So I guess the serialize trick and just updating positions in batch in the controller would not be enough? I also noticed that when you move an item from a list to another, jQuery UI Sortable triggers two update events, one for the source list and the other for the destination list. Thanks a lot in advance for your help!
Reply
Update: actually... it seems to work just fine. The two requests update the two lists correctly with no other changes. Thanks! :)
Reply
Another question.... how would you go about testing sorting with Capybara/system tests? Thanks
Reply

If you know you are not going to respond to our questions, why do you keep the comment session on the blog?

Reply

Hi Chris,

This is such a useful video! With a little help from your friendly neighborhood GoRails Slack channel I got it (mostly) working, but for some reason it's not serializing correctly. According to my server log, it is sending PATCH data to the database:

Started PATCH "/tasks/sort" for 127.0.0.1 at 2018-10-20 11:16:06 -0700
Processing by TasksController#sort as */*
  Parameters: {"task"=>["2", "8"]}
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ /Users/lizbayardelle/.rvm/gems/ruby-2.5.0/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
  Task Update All (0.2ms)  UPDATE "tasks" SET "position" = 1 WHERE "tasks"."id" = ?  [["id", 2]]
  ↳ app/controllers/tasks_controller.rb:27
  Task Update All (0.1ms)  UPDATE "tasks" SET "position" = 2 WHERE "tasks"."id" = ?  [["id", 8]]
  ↳ app/controllers/tasks_controller.rb:27
Completed 200 OK in 3ms (ActiveRecord: 0.5ms)

But for some reason it always stays the original order on refresh. Any hints as to what could be going wrong?

The only difference between my app and the example app is that I had to have the .sortable act on a class (.taskWrapper not #taskWrapper) because I have the tasks in a partial that's being rendered more than one place on the page. Could that be affecting it?

Reply

How to make it work with Mango?

Reply

I can't make it work with serialize: I see the only params sent to my controller are {"controller"=>"tasks", "action"=>"sort"}

Reply

After following along to the end of the video, I stil couldn't get my records(feature_package to update, the params getting passed were Parameters: {"packaged_feature"=>["321", "1"], "id"=>"sort"}, apparently because of the PATCH request it was looking for a specific record id automatically and changing this to a POST in my ajax request and my routes file worked for me.

Reply

Man, that was super-tight. Thank you so much. Really awesome!

Reply

Hi Sir,

Good day!
Can we implement this using Rails 6 webpack?

Reply

plus 1 for this.

Reply

Hay Jaymarc,

run
yarn add jquery-ui

then in your javascript/packs/application.js

require("jquery-ui/ui/widget")
require("jquery-ui/ui/widgets/sortable")

$(document).on("turbolinks:load", () => {
  $("#questions").sortable({
    update: function(e, ui) {
      Rails.ajax({
        url: $(this).data("url"),
        type: "PATCH",
        data: $(this).sortable('serialize'),
      });
    }
  });
})

... and the rest is the same as the tutorial, minus all ui gem. hth

Reply

I get

Uncaught TypeError: $(...).sortable is not a function

yarn add jquery-ui

application.js

require('jquery')
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require('owl.carousel')
require('isotope-layout')
require('packs/redactor.min')
require('packs/filemanager.min')
require('packs/imagemanager.min')
require("jquery-ui/ui/widget")
require("jquery-ui/ui/widgets/sortable")

var jQueryBridget = require('jquery-bridget');
var Isotope = require('isotope-layout');

jQueryBridget( 'isotope', Isotope, $ );


  $(document).on("turbolinks:load", () => {
    $("#document_list").sortable({
      update: function(e, ui) {
        Rails.ajax({
          url:$(this).data('url'),
          type: "PATCH",
          data: $(this).sortable('serialize'),
        });
      }
    });
  })

enviroment.js

 const { environment } = require('@rails/webpacker')

const webpack = require('webpack')
environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery/src/jquery',
    jQuery: 'jquery/src/jquery',
    jquery: 'jquery/src/jquery'
  })

)

module.exports = environment
Reply

This worked for me: https://stackoverflow.com/a/58580434/6430382

Start with removing jquery-ui:

yarn remove jquery-ui
Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 85,376+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.