简体   繁体   中英

Rails - How to split nested attributes through the form?

I understand we can use fields_for to create a subsection of fields for nested attributes. However, I would like to split them through the form. How can I do this?

For instance:

Suppose I have a model foo with a nested bar model, like this:

class Foo < ApplicationRecord
  has_many :bars
  accepts_nested_attributes_for :bars
end

A general view would be something like this:

<%= form_for @foo do |f| %>
  <!-- foo fields -->

  <%= f.fields_for :bars do |f_bar| %>
    <!-- bar fields -->
  <% end %>

  <%= f.submit "Submit" %>
<% end %>

But for aesthetics reasons, I don't want all the bars agglomerated at one place. I would like to do something like:

<%= form_for @foo do |f| %>
  <!-- foo fields -->

  <%= f.fields_for :bars do |f_bar| %>
    <!-- bar fields of one bar -->
  <% end %>

  <!-- other foo fields -->

  <%= f.fields_for :bars do |f_bar| %>
    <!-- bar fields of another bar -->
  <% end %>

  <!-- The previous repeats many more times in a non predictable way -->

  <%= f.submit "Submit" %>
<% end %>

So it would be perfect for me if I didn't have to show all the bars at once. Somebody knows how to that?

You can use the second param of fields_for and pass a scope:

class Bar < ApplicationRecord

  belongs_to :foo

  scope :some_a,->{where(conditions)}
  scope :some_b,->{where(conditions)}

end

In your forms

<%= form_for @foo do |f| %>
    <%= f.text_field :foo_attr %>

    <%= f.fields_for :bars, @foo.bars.some_a do |b| %>
        <%= b.hidden_field :other_bar_attr %>
        <%= b.text_field :bar_attr %>
        ...
    <% end %>

    <%= f.fields_for :bars, @foo.bars.some_b do |b| %>
        <%= b.hidden_field :other_bar_attr %>
        <%= b.text_field :bar_attr %>
        ...
    <% end %>
    <%= f.submit %>
<% end %>

You can use the hidden input for setting default values that are used in the scopes.

UPDATE

If you need to use a multiple instances of fields_for in your form, you can do something like this

In the controller set an array for the scoped objects, an example could be:

class SomeController < AP
  def some_action
    @var_to_the_form = []
    (1..well_know_quantity).each do |value|
      @var_to_the_form << Model.where(conditions)
    end
  end
end

And your form has to be as follows

<% @var_to_the_form.each do |records| %>
  <%= f.fields_for :bars, records do |b| %>
    <%= b.hidden_field :other_bar_attr %>
    <%= b.text_field :bar_attr %>
        ...
  <% end %>
<% end %>

The important part is to know how to set the records you are passing to the view.

So, it happens that all I needed was make fields_for show only one instance per time.

I discovered that fields_for lets you specify a particular object to render the fields. So, I just created a counter and added one @foo.bars[counter] per time and it magically worked, it was something like this :

<% counter = 0 %>
<%= form_for @foo do |f| %>

  <!-- foo fields -->

  <%= f.fields_for :bars, @foo.bars[counter] do |f_bar| %>
    <!-- bar fields of one bar -->
  <% end %>
  <% counter+=1 %>

  <!-- other foo fields -->

  <%= f.fields_for :bars, @foo.bars[counter] do |f_bar| %>
    <!-- bar fields of another bar -->
  <% end %>
  <% counter+=1 %>

  <!-- The previous repeats many more times in a non predictable way -->

  <%= f.submit "Submit" %>
<% end %>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM