Search code examples
ruby-on-railsrubyrefactoringcocoon-gem

Empty link_to_add_association for cocoon nested attributes


I'm using simple_form with cocoon (https://github.com/nathanvda/cocoon) and everything works pretty well.

The only thing I really really dislike is the fact that I have to initialize empty instances to make it work:

def new
  @facility = Facility.friendly.find(params[:facility_slug])
  @pet = @facility.pets.build(animal: Dog.new)
  @pet.pet_pictures.build
  @pet.animal.mixtures.build
end

The last 2 lines are for making cocoon and link_to_add_association work, if I remove them link_to_add_association is completely empty.

Does anybody know how to make this a little bit more idiomatic and avoid explicitly call the build method? How can I improve this code?


Solution

  • How can I improve this code?

    By putting the code in the model:

    #app/models/facility.rb
    class Facility < ActiveRecord::Base
       def construct_pet animal
          model = animal.to_s.constantize
          pet = pets.build animal: model.send(:new)
          pet.pet_pictures.build
          pet.animal_mixtures.build
          pet
       end
    end
    
    #app/controllers/facilities_controller.rb
    class FacilitiesController < ApplicationController
       def new
          @facility = Facility.find(params[:facility_slug]).construct_pet(:dog)
       end
    end
    

    The problem you have is not with cocoon, it's Rails.

    Let me explain:


    fields_for

    You must remember that rails is object orientated, in every sense of the term.

    This means that if you want to create dependent data (IE nested fields), you'll have to build the relevant associated model instances.

    There is no getting around this; if you don't build the models, Rails will simply not know how to construct the fields_for methods.

    When you create an associated model (IE pass data with accepts_nested_attributes_for), Rails has to have instances of the related models to pass.

    If you don't build the dependent models, you don't get any related fields, and thus it won't work.

    Cocoon uses fields_for in exactly the same way as you would if you "manually" did it:

    enter image description here

    You can see from this RailsCast and this answer I literally just wrote.

    --

    avoid explicitly call the build method

    N'est pas possible, mon ami.

    The build method creates instances of the instances associative models. If you don't want them to show (which is required to get fields_for working), you won't be able to use them.