Lee Eather
Lee Eather

Reputation: 355

Rails, cant get update to work with cocoon when removing attribute "undefined method '[]' for nil:NilClass"?

I am wondering if someone could tell me why I am getting this error "undefined method '[]' for nil:NilClass". This happens when I remove a picture with cocoon and try update. The method works fine for adding pictures to the edited gallery but I am getting this error when removing and updating. I tried using unless @pictures.blank? end I am assuming the problem is when cocoon removes the picture but I am not sure what to do from there. the server error is,

Started PATCH "/galleries/41" for ::1 at 2017-05-07 16:03:02 +1000
Processing by GalleriesController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"cG1UXCvODhYzqqAr++EAn8GvMVk7+t/eASkzDDOoPmJfw3l6ax/F2xXMhvs7FcrJ3LOuTd0sks5+2fb86kQv0Q==", "gallery"=>{"name"=>"Hellooo", "cover"=>"123456", "pictures_attributes"=>{"0"=>{"_destroy"=>"1", "id"=>"47"}, "1"=>{"_destroy"=>"1", "id"=>"48"}}}, "commit"=>"Update Gallery", "id"=>"41"}
  Gallery Load (0.0ms)  SELECT  "galleries".* FROM "galleries" WHERE "galleries"."id" = ? LIMIT ?  [["id", 41], ["LIMIT", 1]]
Unpermitted parameter: pictures_attributes
   (0.0ms)  begin transaction
  User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  (0.0ms)  commit transaction
Completed 500 Internal Server Error in 4ms (ActiveRecord: 0.0ms)

NoMethodError (undefined method `[]' for nil:NilClass):
...

Perhaps if someone could explain this to me would be great!

_form.html.erb

<%= form_for(@gallery, multipart: true) do |f| %>
  <div class="field">
    <%= f.label :name %>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :cover %>
    <%= f.text_field :cover %>
  </div>
  <div id="pictures">
    <%= f.fields_for :pictures do |pic| %>
    <%= render 'picture_fields', f: pic %>
  </div>
  <% end %>

    <div class="links">
      <%= link_to_add_association 'add picture', f, :pictures %>
  <%= f.submit %>
  </div>
<% end %>

_picture_fields.html.erb

<div class="nested-fields">
  <div class="field">
    <%= f.label :picture %>
    <%= f.file_field :picture, multiple: true, name: "pictures[picture][]" %>
    <%= link_to_remove_association "remove picture", f %>
  </div>
</div>

GalleriesController

def update
    @gallery = Gallery.find(params[:id])
    if @gallery.update(gallery_params)
       params[:pictures][:picture].each do |pic|
       @pictures = @gallery.pictures.create!(picture: pic)
      end
      flash[:success] = "Gallery Updated!"
      redirect_to root_url
    else
      render 'edit'
    end
  end

Edit: Added gallery_params

def gallery_params
   params.require(:gallery).permit(:id, :name, :user_id, :cover, picture_attributes: [:id, :gallery_id, :picture, :_destroy])
  end

EDIT: Added create action and server log using cocoon

 def create
    @user = User.first
    @gallery = @user.galleries.build(gallery_params)
    if @gallery.save       
       flash[:success] = "Picture created!"
       redirect_to root_url
    else
     render 'new'
   end
 end

server log

Started POST "/galleries" for ::1 at 2017-05-10 13:18:43 +1000
Processing by GalleriesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"XU3z2jMdbselPJZ2SdZdGPwiAebiPznt8GWRqmbv8LM/MIxO+sNo1z2NTaDQ3nJNm0qaBJ66ny5254MPpHZaQQ==", "gallery"=>{"name"=>"Hello", "cover"=>"123456", "pictures_attributes"=>{"1494386318553"=>{"picture"=>#<ActionDispatch::Http::UploadedFile:0xac59228 @tempfile=#<Tempfile:C:/Users/Lee/AppData/Local/Temp/RackMultipart20170510-7596-16xlrir.jpg>, @original_filename="Skateboard 1.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"gallery[pictures_attributes][1494386318553][picture]\"; filename=\"Skateboard 1.jpg\"\r\nContent-Type: image/jpeg\r\n">, "_destroy"=>"false"}, "1494386321001"=>{"picture"=>#<ActionDispatch::Http::UploadedFile:0xac59150 @tempfile=#<Tempfile:C:/Users/Lee/AppData/Local/Temp/RackMultipart20170510-7596-jxo0st.jpg>, @original_filename="Skateboard 2.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"gallery[pictures_attributes][1494386321001][picture]\"; filename=\"Skateboard 2.jpg\"\r\nContent-Type: image/jpeg\r\n">, "_destroy"=>"false"}}}, "commit"=>"Create Gallery"}
  User Load (0.0ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
   (0.0ms)  begin transaction
  User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
   (0.0ms)  rollback transaction
  Rendering galleries/new.html.erb within layouts/application
  Rendered galleries/_picture_fields.html.erb (1.0ms)
  Rendered galleries/_picture_fields.html.erb (0.5ms)
  Rendered galleries/_picture_fields.html.erb (0.5ms)
  Rendered galleries/_form.html.erb (42.0ms)
  Rendered galleries/new.html.erb within layouts/application (58.5ms)
Completed 200 OK in 157ms (Views: 139.3ms | ActiveRecord: 0.0ms)

Upvotes: 0

Views: 595

Answers (2)

nathanvda
nathanvda

Reputation: 50057

Mmmm. Confused. There is no params[:pictures] so that is obviously nil (check the log you posted at the top). So that is causing the error. If you are looking for the pictures posted, you should refer to params[:pictures_attributes], but not even sure what you are trying to do there: create an empty picture (again?) for each posted pictures? The pictures are saved by doing the gallery.update(gallery_params).

Note: iterating over the posted params is imho definitely wrong, because if one is deleted, it will still be posted with the nested parameter _destroy set to true, so it can be deleted correctly from the database, or if a picture already existed, it will also be posted again (and not saved, since it already exists).

[EDIT] Add short solution:

  • use f.fields_for :pictures in your view: this will iterate over the existing pictures a gallery has, and will allow to delete/edit existing, and add new pictures to a gallery
  • fix your gallery_params and allow pictures_attributes (instead of the singular form)(otherwise nothing is saved)
  • in your controller just write @gallery.update_attributes(gallery_params) and do not iterate over params[:pictures] at all (remove that part) and it should just work (because the update_attributes will already have done this, at least if you want to iterate manually use pictures_attributes)

Upvotes: 1

ruby_newbie
ruby_newbie

Reputation: 3285

It looks like your gallery_params method is not permitting your pictures_attributes. You didn't post that code but I noticed in the error log Unpermitted parameter: pictures_attributes which means that your strong parameters(the gallery_params method) is filtering those parameters out.

Basically the whole point of the strong parameters is to make sure that you only pass through keys that you actually want to get passed through. So your controller is like "Nobody told me I am supposed to accecpt picture_attributes so I won't allow them through." Then your code is expecting there to be a picture object in the pictures array, but the pictures array doesn't have the picture object causing an error.

Can you post the code for gallery params? Can you also post the contents of params? Full disclosure: I don't really know rails 5 so there could be some other stuff going on.

Upvotes: 0

Related Questions