#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 предоставляет готовое решение для этого, но я им не пользовался.