0

I have a Rails controller set up that when a user creates a new record, it will redirect again to the new action and populate the form with new data, and this can continue on in a loop, creating a new record and getting redirected to the new action. What I am having trouble with is adding AJAX to this so that the user can stay in this loop without the page reloading. Here is my code without AJAX:

class ResponsesController
 def new
   @response = Response.new
   @answer_a = Answer.find(rand(1..100))
   @answer_b = Answer.find(rand(1..100))
   @answer_c = Answer.find(rand(1..100))
 end

 def create
   @response = current_user.responses.build(params[:response])
   if @response.save
      respond_to do |format|
        format.html { redirect_to new_response_path }
        format.js
      end
   else
      render 'new'
   end
 end
end

new.html.erb

 <div id="response_form">
   <%= render 'form' %>
 </div>

_form.html.erb

 <%= form_for @response do |f| %>   
    <%= f.radio_button :user_answer, @answer_a.id %> 
    <%= @answer_a.description %>
    <%= f.radio_button :user_answer, @answer_b.id %> 
    <%= @answer_b.description %>
    <%= f.radio_button :user_answer, @answer_c.id %> 
    <%= @answer_c.description %>
    <%= f.submit "Answer" %>                
 <% end %>

Below is my attempt to add AJAX. However, it is going to the show action and doing an update. How can I fix my code so that it keeps redirecting to the new action and the user can continue to create new records on an infinite loop?

 def create
   @answer_a = Answer.find(rand(1..100))
   @answer_b = Answer.find(rand(1..100))
   @answer_c = Answer.find(rand(1..100))
   @response = current_user.responses.build(params[:response])
   if @response.save
      respond_to do |format|
        format.html { redirect_to new_response_path }
        format.js
      end
   else
      render 'new'
   end
 end

_form.html.erb

 <%= form_for @response, :remote => true do |f| %>  
    <%= f.radio_button :user_answer, @answer_a.id %> 
    <%= @answer_a.description %>
    <%= f.radio_button :user_answer, @answer_b.id %> 
    <%= @answer_b.description %>
    <%= f.radio_button :user_answer, @answer_c.id %> 
    <%= @answer_c.description %>
    <%= f.submit "Answer" %>                
 <% end %>

create.js.erb

 $('#response_form').html('<%= j render("form") %>');
diasks2
  • 2,033
  • 2
  • 36
  • 61
  • 2
    On a side note, don't get random records like that. You will return nil objects if the record isn't found, and lots of pretty errors, especially when you try to call them with @answer_a.description. See here: http://stackoverflow.com/questions/5342270/rails-3-get-random-record – Damien Roche Nov 08 '12 at 01:58

1 Answers1

3

The code seems fine by me. It shouldn't be redirecting to the show action. Make sure that:

  1. You have gem 'jquery-rails' in your Gemfile

  2. You have <%= javascript_include_tag 'application' %> in your application.html.erb file

  3. You have the following lines in your application.js file

    //= require jquery
    //= require jquery_ujs
    

Ok, so the problem here is that you are including the line <%= form_for @response do |f| %> inside the _form partial. Why is that a problem? Well, when an object @response does not exist, Rails renders the form like this:

<form accept-charset="UTF-8" action="/responses" method="post">

When an object @response does exist, Rails renders the form like this:

<form accept-charset="UTF-8" action="/responses/id_of_the_response" method="post">

Rails does this because it knows that @response exists so it's only logical that you want to update it (and not create it again).

The solution here is to change the form_for to this:

<%= form_for @response, :url => { :action => :create, :method => :post } do |f| %>

Also, be mindful that build is not saving the @response object.

Ashitaka
  • 19,028
  • 6
  • 54
  • 69
  • The first time I click submit, it correctly replaces the HTML, but it then does: `Started PUT "/responses/66" Processing by ResponsesController#update as JS` and `Completed 500 Internal Server Error in 30ms ActionView::MissingTemplate (Missing template responses/show` – diasks2 Nov 08 '12 at 02:05
  • Well, it's saying you are missing the `responses show` view. But the problem here is related to the `update` action. Perhaps you forgot to edit that action? – Ashitaka Nov 08 '12 at 02:10
  • I guess that is the part I do not understand. I want it to keep looping through the new/create action, but when I change the code to use AJAX, why is it trying to go to the update action? – diasks2 Nov 08 '12 at 02:12
  • Thanks for your help. However, when I pull the lines out of the form partial, I get the following error `ActionView::Template::Error (undefined local variable or method 'f'`. Do I need to change my form to a form_tag? – diasks2 Nov 08 '12 at 02:36
  • No, sorry, I forgot about something. I'm going to edit my answer again. – Ashitaka Nov 08 '12 at 02:43
  • Thanks! Almost there I think. That works to render the form the first time, but when I submit the form with AJAX, I still get the following error `Rendered responses/create.js.erb (27.3ms) Completed 500 Internal Server Error in 54ms ActionView::Template::Error (undefined local variable or method 'f'` even though I changed my create.js.erb to be `$('#response_form').html("<%= j render partial: 'form', :locals => {:f => f} %>");` Any ideas? Thanks for your patience. – diasks2 Nov 08 '12 at 02:50
  • Hmm, I get the same error whether I include it or not in the js file. `ActionView::Template::Error (undefined local variable or method 'f'` – diasks2 Nov 08 '12 at 02:57
  • Oh, right, I had forgotten about the js. Thinking better now, just put the `form_for` where it originally was and change it like this `form_for @response, :url => { :action => :create } do |f|` – Ashitaka Nov 08 '12 at 02:58
  • That's it. Also had to add in `, :method => :post`. If you add that into your answer I'll accept it. Thanks for your help! – diasks2 Nov 08 '12 at 03:09
  • Oh, right, because the update form uses `put`, not `post`. I really should be testing stuff as I'm trying to answer it. Well, the answer is edited, glad I could help! – Ashitaka Nov 08 '12 at 03:11
  • @Ashitaka Bravo on this answer, way to stick with it and solve the problem. – Andrew Nov 08 '12 at 03:13