Search code examples
ruby-on-rails-4rails-activerecordnested-formshas-many-throughstrong-parameters

Not sure why the VIEW does't work: Rails 4 nested attributes and has_many :through associaton in a form


I followed this page to build my app:

Rails 4 nested attributes and has_many :through associaton in a form

but it shows NOTHING in my VIEW:

(the weird thing is when i typed "f.fields_for :questionnaire_surveRys do |ff|" instead of the right one, it showed me the ocrrect page.

any suggestions will be greatly appreciated.

here are my Models:

questionnaire.rb

class Questionnaire < ActiveRecord::Base
    has_many :questionnaire_surveys
    has_many :surveys, through: :questionnaire_surveys
    accepts_nested_attributes_for :questionnaire_surveys
end

questionnaire_survey.rb

class QuestionnaireSurvey < ActiveRecord::Base
    belongs_to :questionnaire
    belongs_to :survey
    accepts_nested_attributes_for :survey
end

survey.rb

class Survey < ActiveRecord::Base
  has_many :questions, :dependent => :destroy
  accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true

  has_many :questionnaire_surveys
  has_many :questionnaires, through: :questionnaire_surveys

end

and this is my questionnaire_controller.rb

def new
    @questionnaire = Questionnaire.new
    @surveys = Survey.all

end

def questionnaire_params
    params.require(:questionnaire).permit(:name, questionnaire_surveys_attributes: [:id, survey_attributes:[:id]])
end

this is my _form.html.erb

<%= form_for(@questionnaire) do |f| %>
  <p>
    <%= f.label :name %><br/>
    <%= f.text_field :name %>

    <div class="field">

     <%= f.fields_for :questionnaire_surveys do |ff| %>
       <%= ff.fields_for :survey do |builder| %>
         <% @surveys.each do |survey| %> 
          <%= builder.check_box :id, {}, survey.id %>
          <%= builder.label survey.name %>
         <% end %>
       <% end %>   
     <% end %>
   </div>

  </p>
  <div class="actions">
    <%= f.submit %>
  </div>

UPDATED:

Started POST "/questionnaires" for ::1 at 2015-07-29 22:45:16 +0800
Processing by QuestionnairesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"k4SkRC08PwAHAo1iERmQCkssdQZYgf+uHwofPdeLbXo0O4/psY3Y7i/krQA01omToQ4VLlt/YQDNkcbpLGp86w==", "questionnaire"=>{"name"=>"what just happened", "questionnaire_surveys_attributes"=>{"0"=>{"survey_attributes"=>{"name"=>""}}}}, "commit"=>"Create Questionnaire"}
Unpermitted parameter: name
   (0.1ms)  begin transaction
  SQL (0.7ms)  INSERT INTO "questionnaires" ("name", "created_at", "updated_at") VALUES (?, ?, ?)  [["name", "what just happened"], ["created_at", "2015-07-29 14:45:16.374246"], ["updated_at", "2015-07-29 14:45:16.374246"]]
  SQL (0.2ms)  INSERT INTO "surveys" ("created_at", "updated_at") VALUES (?, ?)  [["created_at", "2015-07-29 14:45:16.377439"], ["updated_at", "2015-07-29 14:45:16.377439"]]
  SQL (0.1ms)  INSERT INTO "questionnaire_surveys" ("questionnaire_id", "survey_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["questionnaire_id", "52"], ["survey_id", "38"], ["created_at", "2015-07-29 14:45:16.378845"], ["updated_at", "2015-07-29 14:45:16.378845"]]
   (0.9ms)  commit transaction
Redirected to http://localhost:3000/questionnaires/52
Completed 302 Found in 12ms (ActiveRecord: 2.0ms)

UPDATE - 2015/7/31

Started POST "/questionnaires" for ::1 at 2015-07-31 17:46:50 +0800
Processing by QuestionnairesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"t/00prIClAUVdqPFxOnkTaxRPhTdY082PAvHb/VQSO4QQh8LLrNz6z2Qg6fhJv3URnNePN6d0ZjukB67DrFZfw==", "questionnaire"=>{"name"=>"OMG", "questionnaire_surveys_attributes"=>{"0"=>{"survey_attributes"=>{"name"=>""}}}}, "commit"=>"Create Questionnaire"}
   (0.2ms)  begin transaction
  SQL (0.7ms)  INSERT INTO "questionnaires" ("name", "created_at", "updated_at") VALUES (?, ?, ?)  [["name", "OMG"], ["created_at", "2015-07-31 09:46:50.440466"], ["updated_at", "2015-07-31 09:46:50.440466"]]
  SQL (0.4ms)  INSERT INTO "surveys" ("name", "created_at", "updated_at") VALUES (?, ?, ?)  [["name", ""], ["created_at", "2015-07-31 09:46:50.446176"], ["updated_at", "2015-07-31 09:46:50.446176"]]
  SQL (0.2ms)  INSERT INTO "questionnaire_surveys" ("questionnaire_id", "survey_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["questionnaire_id", "53"], ["survey_id", "39"], ["created_at", "2015-07-31 09:46:50.450001"], ["updated_at", "2015-07-31 09:46:50.450001"]]
   (0.9ms)  commit transaction
Redirected to http://localhost:3000/questionnaires/53
Completed 302 Found in 22ms (ActiveRecord: 2.4ms)

UPDATE - 2015/8/05

I can't upload pics here, hope this is what you need:

<input placeholder="vision" type="text" name="questionnaire[questionnaire_surveys_attributes][0][survey_attributes][name]" id="questionnaire_questionnaire_surveys_attributes_0_survey_attributes_name">

UPDATE - 2015/8/11

_form.erb.html

 <div class="field">
         <% @surveys.each do |survey| %> 
            <%= check_box_tag "questionnaire[questionnaire_surveys_attributes][][survey_id]", survey.id %>
            <%= label_tag survey.name %>
         <% end %>
</div>

questionnaires_controller.rb

params.require(:questionnaire).permit(:name, questionnaire_surveys_attributes: [:survey_id])

def new
    @questionnaire = Questionnaire.new
    @surveys = Survey.all
end

UPDATE - 2015/8/17

I misused the has_many :through and accepts_nested_attributes_for. In has_many:xxx :through case, there is xxx_ids. In accepts_nested_attributes_for xxx case, there is xxx_attributes.

I used accepts_nested_attributes_for in both questionnaire.rband questionnaire_survey.rb , which is a mistake.

The correct way to do what I want is use the has_many :through only. Then my questionnaire_controller.rb will have

def questionnaire_params
    params.require(:questionnaire).permit(:name, :survey_id=>[])
end

in _form view, it should be

 <%= check_box_tag "questionnaire[survey_id][]", survey.id %>

it's much easier now.

@Rich Peck thanks for all your help.


Solution

  • First things first - if you're not seeing the form elements appear, it's because you've not got it set up correctly in the backend.

    For the longest time, I tried to set this up and was getting very frustrated that the embedded form would not appear. It wasn't until I sorted it out properly that it worked. It's called graceful degradation (I think) - whereby no error will appear, yet functionality will be impaired.


    Firstly, I think you haven't built your associated objects in the controller:

    #app/controllers/questionnaire_controller.rb
    def new
        @questionnaire = Questionnaire.new
    
        # You need to build the associated objects, like this:
        @questionnaire.questionnaire_surveys.build.build_survey
    
        @surveys = Survey.all
    
    end
    

    --

    Secondly, there is a better way to show checkboxes for your @surveys object:

    <%= ff.fields_for :survey do |survey| %>
        <%= survey.collection_check_boxes :survey_ids, @surveys, :id, :name %>
    <% end %>
    

    You can read up about collection_check_boxes here

    --

    Thirdly, you should definitely learn haml. You could write your entire form like this:

    = form_for @questionnaire do |f|
    
        .name
          = f.label :name 
          = f.text_field :name
    
        .field
          = f.fields_for :questionnaire_surveys do |ff| %>
             = ff.fields_for :survey do |survey| %>
               = survey.collection_check_boxes :survey_ids, @surveys, :id, :name
    
        .actions
          = f.submit
    

    --

    Finally, don't use HTML elements as styling.

    <p> & <br> should only be used as markup. If you're using them for styling effect, you'll end up causing problems with browser compatibility etc.

    You need to let your CSS do the styling (colouring, size, position), and any on-page elements used as ways to separate the content of your application.


    Update

    Okay, so I've looked at your BitBucket:

    1. You need to uncomment @questionnaire.questionnaire_surveys.build.build_survey in app/controllers/questionnaires_controller.rb#20

    If you do that, it should work.

    I cannot see any problems with the construct of the models and controllers. Are you sure you've refreshed etc?

    I see you're calling <%= render "form" %> - try putting the form directly in the new view to test if it will work.

    Also, have you tried using a simple way to add the extra fields, like this:

       <%= f.fields_for :questionnaire_surveys do |ff| %>
           <%= ff.fields_for :survey do |builder| %>
             <% @surveys.each do |survey| %> 
               <%= builder.text_field :name, placeholder: survey.name %>
           <% end %>
       <% end %>
    

    Finally, if you post your posted parameters after form submit, I'll be in a much stronger position to see any of the errors/problems you may have.

    --

    You can change your params to the following:

    #app/controllers/questionnaires_controller.rb
    ...
    def questionnaire_params
     params.require(:questionnaire).permit(:name, questionnaire_surveys_attributes: [:id, survey_attributes:[:name]])
    end