开发者

rails process for saving order of awesome_nested_set using jquery & nestedsortables

I have a categories model made with the fantastic awesome_nested set. I have successfully generated the drag and drop tree and successfully generated the complete hash of this tree using SERIALIZELIST plugin and sent it to the "array" method that I have added to my categories controller. (using jquery and nestedsortables) The hash from my log looks like so ...

Processing CategoriesController#array (for 127.0.0.1 at 2010-08-19 23:12:18) [POST] Parameters: {"ul"=>{"0"=>{"class"=>"", "id"=>"category_1", "children"=>{"0"=>{"class"=>"", "id"=>"category_4", "children"=>{"0"=>{"class"=>"", "id"=>"category_3"}}}}}, "1"=>{"class"=>"", "id"=>"category_2", "children"=>{"0"=>{"class"=>"", "id"=>"category_5"}, "1"=>{"class"=>"", "id"=>"category_6"}}}}}

i'm just having trouble with the sort function.

Awesome nested set does provide a few move functions but I can't seem to get my head around it.

I want to do something like this when the user hits save (btw it does an ajax request and passes the above data correctly)

def array
    newlist = params[:ul]
    newlist.each_with_index do |id, index, children|
      #insert code here for saving the re-ordered array
    end
    render :nothing => true
end

I hope this is enough information and hope someone can answer this question.

Cheers,

Matenia

----------- UPDATE AND PROGRESS -----------

Since posting this a few days ago, I mucked around with the logger.info in my dev environment to see what was going on behind the scenes.

I ended up writing 2 functions. One to go through the roots of the array and the other to recursively move the children and childrens children into place. But this ends up with too many database calls (there may be no other way to do it though).

the code looks like so ...

  def array
    # fetch the current tree
    @allcategories = Category.all
    # assign the sorted tree to a variable
    newlist = params[:ul]
    # initialize the previous item
    previous = nil
    #loop through each item in the new list (passed via ajax)
    newlist.each_with_index do |array, index|
      # get the category id of the item being moved
      moved_item_id = array[1][:id].split(/category_/)
      # find the object that is being moved (in database)
      @current_category = Category.find_by_id(moved_item_id)
      # if this is the first item being moved, move it to the root.
      unless previous.nil?
        @previous_item = Category.find_by_id(previous)
        @current_category.move_to_right_of(@previous_item)
      else
        @current_category.move_to_root
      end
      # then, if this item开发者_Python百科 has children we need to loop through them
      unless array[1][:children].blank?
        # unless there are no children in the array, send it to the recursive children function
        childstuff(array[1], @current_category)
      end
      # set previous to the last moved item, for the next round
      previous = moved_item_id
    end
    render :nothing => true
  end
  def childstuff(node, category)
    # find the category that has been passed into the function
    @selected_category = Category.find(category)
    for child in node[:children]
      child_id = child[1][:id].split(/category_/)
      child_category = Category.find_by_id(child_id)
      child_category.move_to_child_of(@selected_category)
      #if this child has children -- run recursion on this function
      unless child[1][:children].blank?
        childstuff(child[1], child_category)
      end
    end
  end

I hope someone can shed some light on how to make this more efficient and how to reduce the number of database calls. I have thought about writing other functions, but theyre all going to do the same thing.

For this particular project, I don't believe there would be more than 100 different categories. It's not the best way, but it works.

Cheers again,

Matenia


THE FINAL WORKAROUND

I had an issue with the above code where it wasn't saving the children properly. Here is my latest attempt, which seems to work well.

def array
    # assign the sorted tree to a variable
    newlist = params[:ul]
    # initialize the previous item
    previous = nil
    #loop through each item in the new list (passed via ajax)
    newlist.each_with_index do |array, index|
      # get the category id of the item being moved
      moved_item_id = array[1][:id].split(/category_/)
      # find the object that is being moved (in database)
      @current_category = Category.find_by_id(moved_item_id)
      # if this is the first item being moved, move it to the root.
      unless previous.nil?
        @previous_item = Category.find_by_id(previous)
        @current_category.move_to_right_of(@previous_item)
      else
        @current_category.move_to_root
      end
      # then, if this item has children we need to loop through them
      unless array[1][:children].blank?
        # NOTE: unless there are no children in the array, send it to the recursive children function
        childstuff(array[1], @current_category)
      end
      # set previous to the last moved item, for the next round
      previous = moved_item_id
    end
    Category.rebuild!
    render :nothing => true
  end
  def childstuff(mynode, category)
   # logger.info "node = #{node} caegory = #{category}"
    #loop through it's children
    for child in mynode[:children]
      # get the child id from each child passed into the node (the array)
      child_id = child[1][:id].split(/category_/)
      #find the matching category in the database
      child_category = Category.find_by_id(child_id)
      #move the child to the selected category
      child_category.move_to_child_of(category)
      # loop through the children if any
      unless child[1][:children].blank?
        # if there are children - run them through the same process
        childstuff(child[1], child_category)
      end
    end
  end

still too many database calls, but I guess that's the price to pay for wanting this functionality as it needs to re-record each item in the database.

Hope this helps someone else in need. Feel free to msg me if anyone wants help with this.

AWESOME NESTED SET + JQUERY DRAG AND DROP + SERIALIZELIST PLUGIN ....

Cheers,

Matenia


see edited question above for final workaround ..I have posted the code on github .. although it may have a few bugs and needs refactoring BADLY! JQUERY NESTED SORTABLES - DRAG AND DROP - AWESOME NESTED SET

UPDATE: Added rails 3 example to repo with slightly cleaner code


The same issue came up when upgrading a rails 2.3 app to 3.1. In my case, I only wanted to sort one depth (root or not). Here's what I ended up with:

# Fetch all IDs (they will be in order)
ids = params[:sort].collect { |param| param[/^page_(\d+)$/, 1] }
# Remove first item from array, moving it to left of first sibling
prev = Page.find(ids.shift)
prev.move_to_left_of(prev.siblings.first)
# Iterate over remaining IDs, moving to the right of previous item
ids.each_with_index { |id, position| current = Page.find(id); current.move_to_right_of(prev); prev = current }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜