RubyOnRails 2.2 + JQuery UI Sortable & AJAX: This is how I did it. Is there a better way?

In a Rails 2.2 project, I am collecting a list of projects in a portfolio (i.e. PortfolioHasManyProjects

). The page provides a Rails form for plain text, titles, etc., as well as 2 sortable lists; lists are used to drag and drop projects from the global project list to your portfolio-project-list.

This is similar to what is done here: http://ui.jquery.com/latest/demos/functional/#ui.sortable .

I have a portfolio list (#drag_list) updated on change and sending its serialized data using an AJAX call. This is done in the application.js file:

jQuery.ajaxSetup({ 
  'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
})

jQuery.fn.submitDragWithAjax = function() {
  this.submit(function() {
    $.post(this.action, $("#drag_list").sortable('serialize'), null, "script");
    return false;
  })
  return this;
};

$(document).ajaxSend(function(event, request, settings) {
  if (typeof(AUTH_TOKEN) == "undefined") return;
  // settings.data is a serialized string like "foo=bar&baz=boink" (or null)
  settings.data = settings.data || "";
  settings.data += (settings.data ? "&" : "") + "authenticity_token=" + encodeURIComponent(AUTH_TOKEN);
});


/-------------------------------------------/

$(document).ready(function(){

    $(".ajax_drag").submitDragWithAjax();

    $("#drag_list").sortable({
        placeholder: "ui-selected",
        revert: true,
        connectWith:["#add_list"],
        update : function () {
            $("#drag_list").submit();
        }
    });

    $("#add_list").sortable({ 
        placeholder: "ui-selected",
        revert: true,
        connectWith:["#drag_list"]
    });

      

Things got complicated here. I was not sure how to deal with the serialized data and submit it to the form to the controller in the file new.html.erb

. So what I did was to new.js.erb

insert the hidden form fields in new.html.erb

with the data that I would retrieve in the controller.

here's new.js.erb:

$("#projects").html("");
<% r = params[:proj] %>
<% order=1 %>
<% for i in r %>
  $("#projects").append("<input type=hidden name=proj[<%=order%>] value=<%=i%> />");
  <% order=order+1 %>
<% end %>

      

which is editing new.html.erb:

<h1>New portfolio</h1>
<h2>The List</h2>

<div class="list_box">
  <h3>All Available Projects</h3>
  <%= render :partial => "projects/add_list" %>
</div>

<div class="list_box">
  <h3>Projects currently in your new portfolio</h3>
  <%= render :partial => "projects/drag_list" %>
</div>

<div style="clear:both"></div>
<br/>
<br/>

<h2>Portfolio details</h2>
<% form_for(@portfolio) do |f| %>
  <%= f.error_messages %>
  <h3>Portfolio Name</h3>
  <p>
    <%= f.text_field :name %>
  </p>
  <h3>URL</h3>
  <p>
    <%= f.text_field :url %>
  </p>
  <h3>Details</h3>
  <p>
    <%= f.text_area :details %>
  </p>
  <p>

    <div id="projects">
    <input type="hidden" name="proj" value="" />   
    </div>

    <%= f.submit "Create" %>
  </p>
<% end %>

      

The form then submits to the create method in the portfolio controller:

  def new
    @projects = Project.find(:all)
    @portfolio = Portfolio.new
    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @portfolio }
      format.js
    end
  end




 def create
    @portfolio = Portfolio.new(params[:portfolio])
    proj_ids = params[:proj]
    @portfolio.projects = []
    @portfolio.save

    proj_ids.each {|key, value| puts "Key:#{key} , Value:#{value} " }
    proj_ids.each_value {|value| @portfolio.projects << Project.find_by_id(value) }

    respond_to do |format|
      if @portfolio.save
        flash[:notice] = 'Portfolio was successfully created.'
        format.html {  render :action => "index" }
        format.xml  { render :xml => @portfolio, :status => :created, :location => @portfolio }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @portfolio.errors, :status => :unprocessable_entity }
      end
    end
  end

      


So finally my question is:

  • Is this the correct way to do it? For some reason, I feel like this is not the case, mainly because doing everything else in Rails is so much easier and more intuitive. It works, but it was damn good. There should be a more elegant way to send serialized data to the controller via AJAX calls.

  • How can I call different AJAX actions on the same page? Let's say I had a sortable and autocomplete AJAX call, can I have sortable.js.erb

    and autocomplete.js.erb

    and call them from any file? I'm not sure how to set up the controllers to answer this.

+1


source to share


2 answers


This is a nice solution if you are using jQuery.

From the linked blog:



I just wrote some sortable code for a Rails / jQuery application, and figured I would blog about how little code it takes, as well as the only MySQL query I used on the server.

+2


source


Here's my solution based on the article mentioned by Silviu. I am sorting the parts that belong to lessons, hence including lessonID.

This is in the view - I am using HAML, so you will need to convert it to erb.

#sorter
- @lesson.parts.each do |part|
  %div[part] <- HAML rocks - this constructs a div <div id="the_part_id" class="part">
    = part_screenshot part, :tiny
    = part.swf_asset.filename

      

Js looks like this:

    $('#sorter').sortable({items:'.part', containment:'parent', axis:'y', update: function() {
  $.post('/admin/lessons/' + LessonId + '/parts/sort', '_method=post&authenticity_token='+ AUTH_TOKEN+'&'+$(this).sortable('serialize'));
  $('#sorter').effect("highlight");
}});

      

and here is the method that is called in the ControlController:



def sort
load_lesson
part_positions = params[:part].to_a
@parts.each_with_index do |part, i|
  part.position = part_positions.index(part.id.to_s) + 1
  part.save
end
render :text => 'ok'

      

end

def load_lesson
@lesson = Lesson.find(params[:lesson_id])
@parts = @lesson.parts

      

end

It needs some work of providing feedback to the user, but the trick is for me.

0


source







All Articles