Sortable Drag and Drop Discussion
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
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.
I'd DEFINITELY be interested in the Vuejs version - I've got an app in mind that it would be very helpful for...
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.
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
(1) When I use:
Rails.ajax
I changed to:
$.ajax
(2) Instead of
//= require jquery-ui/widget //= require jquery-ui/sortable
I needed to use
//= require jquery-ui/widgets/sortable
(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!
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.
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'),
});
}
});
Hope you enjoy it!
If you know you are not going to respond to our questions, why do you keep the comment session on the blog?
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?
I can't make it work with serialize
: I see the only params sent to my controller are {"controller"=>"tasks", "action"=>"sort"}
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.
Hi Sir,
Good day!
Can we implement this using Rails 6 webpack?
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
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
This worked for me: https://stackoverflow.com/a/58580434/6430382
Start with removing jquery-ui:
yarn remove jquery-ui