brendon / acts_as_list

An ActiveRecord plugin for managing lists.

Home Page:http://brendon.github.io/acts_as_list/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Change scope AND send to top/bottom with one update

tjwallace opened this issue · comments

Is it possible to change the scope of an item AND move it to the top/bottom of the new list in one update? Right now it seems like I have to first update the scope and then call #move_to_top or #move_to_bottom.

From looking at the code it seems like this code would have to be modified?

commented

Also, came here looking for the same. It is also unclear how position collisions are handled when scope is changed - should it default to being added to top or bottom?

Haha, that method is kinda gross eh! I wrote that a long time ago.

So if you just change the scope, it's my understanding that the record will be appended to the bottom of the list by default (or the top if you configure that to be the add_new_at). Does this work for you?

@oyeanuj, you've been busy! :) I have your other comments open to look at in the minute, but you're right, the default is for the record to be appended to the bottom of the new scope (I'm fairly sure! - I haven't double-checked the code)

Just thinking this through (sorry I don't have the time to try this stuff out in the console), but you may have to nullify the position column when changing the scope if you want the item to be appended to the end of the list.

After playing around with the library a bit more, by default changing the scope will move the item to whatever add_new_at is set to unless the position has been explicitly been changed.

commented

@brendon yes, implementing drag and drop in my frontend brought me back here :) thanks for all your responses!

And, yes this behavior makes sense!

@tjwallace thanks for checking that and reporting back!

@tjwallace, you're right! We detect a change on the position column. So setting it to NULL might not work? Certainly, not changing it has the desired effect anyway :)

@oyeanuj, for drag and drop, I find it's easier to just serialise the entire list again manually with something like this.

# I store this in /app/models/extensions/sortable.rb (it'll autoload)
module Sortable
  def sort(array_of_ids)
    array_of_ids.each.with_index(1) do |id, index|
      where(:id => id).update_all(:position => index)
    end
  end
end

# This is your model. We extend the relationship with methods from the above module.
class FaqArea < ActiveRecord::Base
  has_many :faqs, -> { extending(Sortable).order(:position) }, :dependent => :destroy
end

# In your controller: params[:faqs] is an array with the id's of the items in your scope ordered as you want them in the array. e.g. [5, 8, 2, 4, 1]. The assumption is that you're sending all the id's within the given scope (which in this case is faq_area_id = 1234).
def sort
  faqs = FaqArea.find(1234).faqs
  faqs.sort params[:faqs]
  render :nothing => true
end

I hope that helps :) It's a bit of a sledgehammer but does have the side-effect of ensuring the positions are sequential and not-sparse. This approach can fail horribly with concurrent users, though in that case you'd probably want to be using a WebSocket to ensure the order of items is kept in sync between users anyway.

commented

@brendon Thanks for that tip! It gets complicated with pagination, but I'll keep this approach in mind :)

Definitely @oyeanuj, with paginated views, the best you can do is interpret the drop and find the nearest item below the dropped item (or nil if it's dropped in last place) and send the id of the dropped item and the id (or nil) of the next item in the list to the server, then proceed as per my commend in the other issue (position relative to that item).