nathanvda / cocoon

Dynamic nested forms using jQuery made easy; works with formtastic, simple_form or default forms
http://github.com/nathanvda/cocoon
MIT License
3.08k stars 385 forks source link

Persisting Local Variables in Partial #544

Closed nbaki closed 5 years ago

nbaki commented 5 years ago

I understand the way to pass local variables to the partial is to add render_options: {locals: {}} in the link_to_add_association.

However, once the form has been saved and you go back to edit, the local variables are undefined. It's apparent that the local variables aren't being passed to partial because I have not clicked the "Add" button, the saved nested entries are appearing on the page.

How do I resolve this? I get undefined local variable or method equipment_type..

_form.html.erb

  <%= f.inputs 'Hardware', id: 'hardware-containier' do %>
    <%= f.semantic_fields_for :equipments do |equipment| %>
      <%= render 'equipment_fields', f: equipment %>
    <% end %>
    <li class="links">
      <%= link_to_add_association 'Add Hardware', f, :equipments, {class: 'btn btn-link', render_options: {locals: {equipment_type: 'hardwares'}}} %>
    </li>
  <% end %>

_equipment_fields.html.erb:

<div class="nested-fields">
    <%= f.inputs do %>
      <%= f.input :equipment_id, as: :select, collection: GeneralDomain::Equipment.send(equipment_type) %>
      <%= f.input :quantity %>
    <% end %>
    <li><%= link_to_remove_association "Remove #{equipment_type.singularize}", f, class: 'btn btn-link pull-right' %></li>
</div>
Mirv commented 5 years ago

Apologies in direct answer to the question in your title - if the rest didn't directly answer it - you have to have matching fields in a model to persist data. Once you hit "submit/save/send" it goes to the server. Once in the server you have to setup someplace in the model to save it & white list it via strong_param's so the parent form/controller can submit to the correct model.


Since you didn't mention it - did you read the readme on strong_params?

If you did allow for that already - can you post your model & controller code for the variables in those fields?

Also - post the last 5 lines of the error trace since you've got that GeneralDomain:: in there ... as the error message you posted could be as simple as forgetting to include your service class (GeneralDomain) in the controller.

... but we can't tell until you post model/controller & a bit of the stacktrace.

Also though ... syntax~wise

Put your collection in the controller like any other data being passed to the view ...

def action_name
  @service_object = GeneralDomain::Equipment.send(equipment_type)
end

Then do a

<%= f.input :equipment_id, as: :select, collection: @service_object %>

Also, how well tested is your GeneralDomain::Equipment class independently of your app? Have you used it before in other applications?

nbaki commented 5 years ago

Strong params are being used, the data is all saving correctly, and the model is valid. I have tested all of these already.

Here's my models anyway:

procurement.rb

module GeneralDomain
  class Procurement < ActiveRecord::Base
    has_many :equipments, dependent: :destroy
    accepts_nested_attributes_for :equipments, allow_destroy: true
  end
end

equipment.rb

module GeneralDomain
  class Equipment < ActiveRecord::Base
    self.inheritance_column = :_type_disabled

    scope :hardwares, -> {where(type: 'Hardware')}
    scope :softwares, -> {where(type: 'Software')}

    validates_presence_of :name
  end
end

And the reason I can't put the variable in my controller is because the form I'm building contains two sections to add equipment (software & hardware). See below for updated view.

  <%= f.inputs 'Hardware', id: 'hardware-containier' do %>
    <%= f.semantic_fields_for :equipments do |equipment| %>
      <%= render 'equipment_fields', f: equipment %>
    <% end %>
    <li class="links">
      <%= link_to_add_association 'Add Hardware', f, :equipments, {class: 'btn btn-link', render_options: {locals: {equipment_type: 'hardwares'}}} %>
    </li>
  <% end %>

  <%= f.inputs 'Software', id: 'software-container' do %>
    <%= f.semantic_fields_for :equipments do |equipment| %>
      <%= render 'equipment_fields', f: equipment %>
    <% end %>
    <li class="links">
      <%= link_to_add_association 'Add Software', f, :equipments, {class: 'btn btn-link', render_options: {locals: {equipment_type: 'softwares'}}} %>
    </li>
  <% end %>

So hopefully as you can see in the form view, Im building two nested associations allowing users to add hardware and software in different sections of the form.

Here's the error and stacktrace:

undefined local variable or methodequipment_type' for #<#:0x007fa26ed8e538>`

app/views/procurements/_equipment_fields.html.erb:3:in `block in _app_views_procurements__equipment_fields_html_erb__2788720966033358807_70167698460900'
app/views/procurements/_equipment_fields.html.erb:2:in `_app_views_procurements__equipment_fields_html_erb__2788720966033358807_70167698460900'
app/views/procurements/_form.html.erb:15:in `block (3 levels) in _app_views_procurements__form_html_erb__3897994984608305389_70167755144780'
app/views/procurements/_form.html.erb:14:in `block (2 levels) in _app_views_procurements__form_html_erb__3897994984608305389_70167755144780'
app/views/procurements/_form.html.erb:13:in `block in _app_views_procurements__form_html_erb__3897994984608305389_70167755144780'
app/views/procurements/_form.html.erb:1:in `_app_views_procurements__form_html_erb__3897994984608305389_70167755144780'
lib/monkies/aa_views_pages_base.rb:62:in `block in build_page_content'
lib/monkies/aa_views_pages_base.rb:60:in `build_page_content'
app/ui/stewgle/support/procurements.rb:39:in `edit'
app/controllers/application_controller.rb:47:in `set_time_zone'
nathanvda commented 5 years ago

The render_options are passed along to the render call of your partial. When the render is called for persisted items, you are actually in full control (your view code calls that render). So I am guessing in your case, you will want to write something like:

    <%= f.semantic_fields_for :equipments do |equipment| %>
      <%= render 'equipment_fields', f: equipment, equipment_type: 'softwares'  %>
    <% end %>
nbaki commented 5 years ago

@nathanvda That was it. I needed the local variables in both places. Thank you.