pinkfloyd90
pinkfloyd90

Reputation: 678

In Rails, how would you build an array of hashes in the View to pass to the controller?

I'm building a platform that let's user create Projects. Each Project may have different Roles to be covered, and each Role may have different Requirements that the user applying to it has to meet (a Requirement is a pair of Technology and Level objects).

As of now, I have something like this:

<%= simple_form_for([ @project, @role ], remote: true) do |f| %>
   <%= simple_fields_for :requirement do |ff| %>
      <%= ff.input :technology_id, collection: Technology.all, label:false, prompt: "Tecnología" %>
      <%= ff.input :level_id, collection: Level.all, label:false, prompt: "Nivel" %>
   <% end %>

   <%= f.input :name, label:false, placeholder: "Nombre del rol" %>
   <%= f.input :description, label:false, placeholder: "Descripción" %>
   <%= f.submit "Add role", class: "btn btn-primary" %>
<% end %>

However, that approach is not convenient because it will only let me create 1 Requirement (Tech and Level pair) for that Role.

How could I let the user create many different Requirements for that Role at once? I was thinking of building an array of hashes and passing it to the controller so I could then iterate over it. Something like:

requirements = [{technology: "Ruby", level: "Junior"}, {technology: "Rails", level: "Junior"}..]

However, I don't know if that would be the way to do it, and if so if that can be done with simple_form. Any insight would be appreciated.

Upvotes: 0

Views: 28

Answers (1)

max
max

Reputation: 102026

You don't need to do any silly shenanigans here. Just need to change the association to a one-to-many:

class Role < ApplicationRecord
  has_many :requirements
  accepts_nested_attributes_for :requirements
end

class Requirement < ApplicationRecord
  belongs_to :role
  belongs_to :level
  belongs_to :technology
end
<%= simple_form_for([ @project, @role ], remote: true) do |f| %>
   <%= simple_fields_for :requirements do |ff| %>
      <%= ff.input :technology_id, collection: Technology.all, label:false, prompt: "Tecnología" %>
      <%= ff.input :level_id, collection: Level.all, label:false, prompt: "Nivel" %>
   <% end %>
   ...
<% end %>

You then need to seed the association in the controller in order for the inputs to show up:

def new
  @role = Role.new do |r|
    5.times { r.requirements.new }
  end
end

You can whitelist an array of nested attributes by passing an array of symbols corresponding to the attributes you want to permit:

def role_params
  params.require(:role)
        .permit(
           :name, :description,
           requirements_attributes: [
             :id, :_destroy, :technology_id, :level_id
           ]
         )
end

You don't need to loop through anything. accepts_nested_attributes_for :requirements creates a requirements_attributes= setter that takes care of it all for you.

Upvotes: 1

Related Questions