В Rails, как бы вы создали массив хэшей в представлении для передачи контроллеру?

#arrays #ruby-on-rails #model-view-controller #hash

#массивы #ruby-on-rails #модель-представление-контроллер #хэш

Вопрос:

Я создаю платформу, которая позволяет пользователям создавать проекты. У каждого проекта могут быть разные роли, которые необходимо охватить, и у каждой роли могут быть разные требования, которым должен соответствовать пользователь, обращающийся к нему (требование — это пара объектов технологии и уровня).).

На данный момент у меня есть что-то вроде этого:

 <%= 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 %>
  

Однако такой подход не удобен, потому что он позволит мне создать только 1 требование (пару технологий и уровней) для этой роли.

Как я мог бы позволить пользователю создавать много разных требований для этой роли одновременно? Я думал о создании массива хэшей и передаче его контроллеру, чтобы я мог затем выполнить итерацию по нему. Что-то вроде:

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

Однако я не знаю, будет ли это правильным способом, и если да, то можно ли это сделать с помощью simple_form . Любая информация была бы оценена.

Ответ №1:

Вам не нужно делать никаких глупых махинаций здесь. Просто нужно изменить ассоциацию на один ко многим:

 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 %>
  

Затем вам нужно заполнить ассоциацию в контроллере, чтобы отображались входные данные:

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

Вы можете внести массив вложенных атрибутов в белый список, передав массив символов, соответствующих атрибутам, которые вы хотите разрешить:

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

Вам не нужно ничего перебирать. accepts_nested_attributes_for :requirements создает requirements_attributes= сеттер, который позаботится обо всем за вас.

Комментарии:

1. Спасибо @max. Есть ли способ не фиксировать количество требований, передаваемых в представлении, до X элементов? (таким образом, пользователь может генерировать столько, сколько ему нужно). Если нет, я предполагаю, что нет проблем, если из 5 некоторые останутся пустыми?

2. Вам нужно использовать параметр ‘reject_if:’ для accepts_nested_atteibutes_for, если вы хотите отклонить «неполные» записи.

3. Я полагаю, что вы спрашиваете, можете ли вы сделать так, чтобы пользователь мог динамически добавлять больше входных данных — да, это возможно, но это нужно сделать с помощью JavaScript — обычно вы добавляете кнопку, которая добавляет больше входных данных в документ. Существует довольно много способов решить эту проблему: от простого объединения строк до использования Ajax для повторного использования шаблонов rail, и это несколько выходит за рамки вашего первоначального вопроса.

4. Я также считаю, что cocoon gem предоставляет готовое решение для этого, но я им не пользовался.