Jake Smith
Jake Smith

Reputation: 2813

remote: true option for form_for working for new action, but not create action

Update:

This has been resolved! This was a difficult one as it wasn't very easy to debug at all. But in noticing that I had a form tag within a form tag, my gracious "answerer" pointed out that this needed to be fixed regardless of whether this was the root cause of the problem or not. Please keep in mind that I agree with him that nested attributes is definitely the way to go on this one, however because this is a school project at this point in time, I elected to move the inner form outside the outer form for now to get it doing what I expected. This involved changing some of the code you see below. But I will not post that new code because, again, I agree that nested attributes is the way to go in this situation. When I get the time, that will be my next goal for this project.

Original Question:

I'm using Rails 4 and I have nested resources like this:

resources :data do
  resources :todo_items
end

In the edit page of a datum object, I intend for the user to have the ability to add todo_items to the datum object via ajax. When you click on the "add todo item" button, my form for creating a new todo_item is requested, and upon filling out the required fields, the user is able to click add and I intend for another request to be made that will display the todo_item as a partial that includes a checkbox for the user to show whether it is complete or not. The new form comes up fine, but I get an ActionController::UnknownFormat error when I click the add button to display the newly created todo_item. Here is the code I'm using:

app/views/data/_form.html.erb

...
<%= link_to 'Add ToDo', new_datum_todo_item_path(@datum.id), remote: true %>
...

app/controllers/todo_items_controller.rb

class TodoItemsController < ApplicationController
  def new
    @datum = Datum.find(params[:datum_id])
    @todo = TodoItem.new
    respond_to do |format|
      format.js
    end
  end

  def create
    @datum = Datum.find(params[:datum_id])
    @todo = @datum.todo_items.create!(todo_item_params)
    respond_to do |format|
      format.js
    end
  end

  private

    def todo_item_params
      params.require(:todo_item).permit(:task, :content, :done)
    end
end

app/views/todo_items/_form.html.erb (the 'new' form)

<div id="<%= datum.id %>-<%= todo.id %>" class="panel todo-item">
  <%= bootstrap_form_for([datum, todo], remote: true) do |f| %>
    <%= render 'shared/error_messages', object: f.object %>
    <%= f.hidden_field :done, value: false %>
    <%= f.text_field :task, hide_label: true, placeholder: 'task' %>
    <%= f.text_area :content, hide_label: true, placeholder: 'content' %>
    <%= f.submit 'Add', class: 'btn btn-primary btn-sm' %>
  <% end %>
</div>

app/views/todo_items/_todo.html.erb

<%= bootstrap_form_for([datum, todo], remote: true) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <%= f.check_box :done, hide_label: true %>
  <div><%= todo.task %></div>
  <div><%= todo.content %></div>
<% end %>

app/views/todo_items/new.js.erb (the response that is working)

$('.panel-body').append("<%= escape_javascript(render partial: 'form', locals: { datum: @datum, todo: @todo }) %>");

app/views/todo_items/create.js.erb (the response that is not working)

$("#<%= @datum.id %>-<%= @todo.id %>").html("<%= escape_javascript(render partial: 'todo_items/todo', locals: { datum: @datum, todo: @todo }) %>");

Could someone help me figure out what I'm doing wrong here? I only intend to respond_to format.js. Both forms include remote: true. I feel like I'm missing something pretty silly here, but I have been trying to figure this out for a long time now. Thanks! Please let me know if I need to provide any further information...

Update:

Here is what appears in my server log with the new and create requests:

Started GET "/data/532799016a616b1786040000/todo_items/new" for 127.0.0.1 at 2014-03-20 16:05:04 -0600
Processing by TodoItemsController#new as JS
  Parameters: {"datum_id"=>"532799016a616b1786040000"}
  MOPED: 127.0.0.1:27017 COMMAND      database=admin command={:ismaster=>1} runtime: 0.4793ms
  MOPED: 127.0.0.1:27017 QUERY        database=datums_app_development collection=data selector={"_id"=>BSON::ObjectId('532799016a616b1786040000')} flags=[] limit=0 skip=0 batch_size=nil fields=nil runtime: 0.4106ms
  Rendered shared/_error_messages.html.erb (0.6ms)
  Rendered todo_items/_form.html.erb (8.8ms)
  Rendered todo_items/new.js.erb (14.3ms)
Completed 200 OK in 39ms (Views: 15.8ms | ActiveRecord: 0.0ms)


Started POST "/data/532799016a616b1786040000/todo_items" for 127.0.0.1 at 2014-03-20 16:05:11 -0600
Processing by TodoItemsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"stsyxQfAuzPa1BGnOx2cAuagZqFuSHF6jfos9/euw94=", "todo_item"=>{"done"=>"false", "task"=>"Task", "content"=>"Content"}, "commit"=>"Add", "datum_id"=>"532799016a616b1786040000"}
  MOPED: 127.0.0.1:27017 QUERY        database=datums_app_development collection=data selector={"_id"=>BSON::ObjectId('532799016a616b1786040000')} flags=[] limit=0 skip=0 batch_size=nil fields=nil runtime: 0.5380ms
  MOPED: 127.0.0.1:27017 UPDATE       database=datums_app_development collection=data selector={"_id"=>BSON::ObjectId('532799016a616b1786040000')} update={"$push"=>{"todo_items"=>{"_id"=>BSON::ObjectId('532b66176a616b4160010000'), "task"=>"Task", "content"=>"Content", "done"=>false, "updated_at"=>2014-03-20 22:05:11 UTC, "created_at"=>2014-03-20 22:05:11 UTC}}} flags=[]
                         COMMAND      database=datums_app_development command={:getlasterror=>1, :w=>1} runtime: 1.1161ms
Completed 406 Not Acceptable in 8ms

ActionController::UnknownFormat - ActionController::UnknownFormat:
  actionpack (4.0.2) lib/action_controller/metal/mime_responds.rb:372:in `retrieve_collector_from_mimes'
  actionpack (4.0.2) lib/action_controller/metal/mime_responds.rb:189:in `respond_to'
  app/controllers/todo_items_controller.rb:13:in `create'
  actionpack (4.0.2) lib/action_controller/metal/implicit_render.rb:4:in `send_action'
  actionpack (4.0.2) lib/abstract_controller/base.rb:189:in `process_action'
  actionpack (4.0.2) lib/action_controller/metal/rendering.rb:10:in `process_action'
  actionpack (4.0.2) lib/abstract_controller/callbacks.rb:18:in `block in process_action'
  activesupport (4.0.2) lib/active_support/callbacks.rb:403:in `_run__1881092772621756057__process_action__callbacks'
  activesupport (4.0.2) lib/active_support/callbacks.rb:80:in `run_callbacks'
  actionpack (4.0.2) lib/abstract_controller/callbacks.rb:17:in `process_action'
  actionpack (4.0.2) lib/action_controller/metal/rescue.rb:29:in `process_action'
  actionpack (4.0.2) lib/action_controller/metal/instrumentation.rb:31:in `block in process_action'
  activesupport (4.0.2) lib/active_support/notifications.rb:159:in `block in instrument'
  activesupport (4.0.2) lib/active_support/notifications/instrumenter.rb:20:in `instrument'
  activesupport (4.0.2) lib/active_support/notifications.rb:159:in `instrument'
  actionpack (4.0.2) lib/action_controller/metal/instrumentation.rb:30:in `process_action'
  actionpack (4.0.2) lib/action_controller/metal/params_wrapper.rb:245:in `process_action'
  activerecord (4.0.2) lib/active_record/railties/controller_runtime.rb:18:in `process_action'
  actionpack (4.0.2) lib/abstract_controller/base.rb:136:in `process'
  actionpack (4.0.2) lib/abstract_controller/rendering.rb:44:in `process'
  actionpack (4.0.2) lib/action_controller/metal.rb:195:in `dispatch'
  actionpack (4.0.2) lib/action_controller/metal/rack_delegation.rb:13:in `dispatch'
  actionpack (4.0.2) lib/action_controller/metal.rb:231:in `block in action'
  actionpack (4.0.2) lib/action_dispatch/routing/route_set.rb:80:in `dispatch'
  actionpack (4.0.2) lib/action_dispatch/routing/route_set.rb:48:in `call'
  actionpack (4.0.2) lib/action_dispatch/journey/router.rb:71:in `block in call'
  actionpack (4.0.2) lib/action_dispatch/journey/router.rb:59:in `call'
  actionpack (4.0.2) lib/action_dispatch/routing/route_set.rb:680:in `call'
  rack (1.5.2) lib/rack/etag.rb:23:in `call'
  rack (1.5.2) lib/rack/conditionalget.rb:35:in `call'
  rack (1.5.2) lib/rack/head.rb:11:in `call'
  actionpack (4.0.2) lib/action_dispatch/middleware/params_parser.rb:27:in `call'
  actionpack (4.0.2) lib/action_dispatch/middleware/flash.rb:241:in `call'
  rack (1.5.2) lib/rack/session/abstract/id.rb:225:in `context'
  rack (1.5.2) lib/rack/session/abstract/id.rb:220:in `call'
  actionpack (4.0.2) lib/action_dispatch/middleware/cookies.rb:486:in `call'
  activerecord (4.0.2) lib/active_record/query_cache.rb:36:in `call'
  activerecord (4.0.2) lib/active_record/connection_adapters/abstract/connection_pool.rb:626:in `call'
  activerecord (4.0.2) lib/active_record/migration.rb:369:in `call'
  actionpack (4.0.2) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
  activesupport (4.0.2) lib/active_support/callbacks.rb:373:in `_run__936815074203488114__call__callbacks'
  activesupport (4.0.2) lib/active_support/callbacks.rb:80:in `run_callbacks'
  actionpack (4.0.2) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
  actionpack (4.0.2) lib/action_dispatch/middleware/reloader.rb:64:in `call'
  actionpack (4.0.2) lib/action_dispatch/middleware/remote_ip.rb:76:in `call'
  better_errors (1.0.1) lib/better_errors/middleware.rb:84:in `protected_app_call'
  better_errors (1.0.1) lib/better_errors/middleware.rb:79:in `better_errors_call'
  better_errors (1.0.1) lib/better_errors/middleware.rb:56:in `call'
  actionpack (4.0.2) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call'
  actionpack (4.0.2) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
  railties (4.0.2) lib/rails/rack/logger.rb:38:in `call_app'
  railties (4.0.2) lib/rails/rack/logger.rb:20:in `block in call'
  activesupport (4.0.2) lib/active_support/tagged_logging.rb:67:in `block in tagged'
  activesupport (4.0.2) lib/active_support/tagged_logging.rb:25:in `tagged'
  activesupport (4.0.2) lib/active_support/tagged_logging.rb:67:in `tagged'
  railties (4.0.2) lib/rails/rack/logger.rb:20:in `call'
  actionpack (4.0.2) lib/action_dispatch/middleware/request_id.rb:21:in `call'
  rack (1.5.2) lib/rack/methodoverride.rb:21:in `call'
  rack (1.5.2) lib/rack/runtime.rb:17:in `call'
  activesupport (4.0.2) lib/active_support/cache/strategy/local_cache.rb:83:in `call'
  rack (1.5.2) lib/rack/lock.rb:17:in `call'
  actionpack (4.0.2) lib/action_dispatch/middleware/static.rb:64:in `call'
  rack (1.5.2) lib/rack/sendfile.rb:112:in `call'
  railties (4.0.2) lib/rails/engine.rb:511:in `call'
  railties (4.0.2) lib/rails/application.rb:97:in `call'
  rack (1.5.2) lib/rack/lock.rb:17:in `call'
  rack (1.5.2) lib/rack/content_length.rb:14:in `call'
  rack (1.5.2) lib/rack/handler/webrick.rb:60:in `service'
  /home/jake/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/webrick/httpserver.rb:138:in `service'
  /home/jake/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/webrick/httpserver.rb:94:in `run'
  /home/jake/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/webrick/server.rb:295:in `block in start_thread'

Upvotes: 2

Views: 3083

Answers (3)

markets
markets

Reputation: 7043

One possible error is related to dom objects selectors. Try with:

$('#<%= @datum.id %>-<%= @todo.id %>').html("<%= escape_javascript(render partial: 'todo_items/todo', locals: { datum: @datum, todo: @todo }) %>");

Using jquery selector by #id instead of by .class. Since you have added instances info (id="<%= datum.id %>-<%= todo.id %>") to id attribute:

<div id="<%= datum.id %>-<%= todo.id %>" class="panel todo-item">

Update after review source code linked in comments

You are using a form inside a form and that's incorrect, it's unsupported by browsers and it could cause unpredictable behavior, like in your case.

You should change a bit your approach/implementation. In this case you probably need to work with accepts_nested_attributes_for and fields_for.

Here a related RailsCast.

Upvotes: 1

kikito
kikito

Reputation: 52698

I think rail's remote: true option is "black magic" and I never use it - It's more trouble than it's worth. Your question is a good example.

If I where you, I would use regular javascript instead of using that particular rails bit. It's more verbose, but also more clear, and easier to debug.

I can't program it for you, though.

Good luck!

Upvotes: 1

brianthecoder
brianthecoder

Reputation: 517

The key is here Processing by TodoItemsController#create as HTML. In your create action you're only responding to the js mime-type but the request is being processed as html. Hence the unknown format error. Either add format.html to the action or you can force it to be a js mime type by add format: :js to your form.

Upvotes: -1

Related Questions