Hello everyone,
first of all, i am aware that this is a bit of an advanced issue here and i’ve read the “this is not stackoverflow” warnings. - However, i am desperate enough to give it a try, anyway…
The issue i have might be specific, but i still think it may be quite common as nested forms are everywhere nowadays…
i’ve discussed this with a great guy (tbuehlmann - a very nice and helpfull guy) from the rails irc channel on freenode and. unfortunately he had no time to take a deeper look but he suggested two possible solutions:
- change the form so, that it’ll render dependent on the achievementable type…
my poor attempt looked like this (but doesnt work):
<% @achievements.each do |achievement| %>
<% if achievement.achievementable.is_a?(Domain) %>
<%= domain.fields_for :achievements do |f| %>
<%= render 'domains/achievements', f: f %>
<% end %>
<% elsif achievement.achievementable.is_a?(ActionField) %>
<%= domain.fields_for(:action_fields) { |f| f.fields_for :achievements do |achievement|
render 'domains/achievements', f: achievement
end } %>
<% end %>
<% end %>
i am aware that this wont work as this block gets executed for each action_field achievement, which results in duplicate table rows… however, i am not even sure if this is the right approach or if i should
or
- write your own setter method - e.g. write / override the “achievements_attributes=(achievement” method inside the domains_controller.
i’ve googled the heck out of it but all i could come up with is how to set specific attributes of an already “identified” object…
i guess in my case i somehow got to tell rails first, where it could find the object if it isnt related to the domain i am viewing… so, similar to the view approach i probably gotta to have 2 paths, if its a domain achievement, all is fine, if its an action_field achievement, i need to tell it that it needs to look for an actin_field achievement and not for a domain_achievement.
however, i have no idea how to proceed from here… and any suggestion will help!
thank you guys,
Andreas
this is the post from (ironically ) stackoverflow…:
i am stuck with a rails problem and a 2-level nested form… I am getting the error:
ActiveRecord::RecordNotFound - Couldn’t find Achievement with ID=10 for Domain with ID=1:
which somehow is correct as that achievement is’nt from the domain but from one of the domain’s ActionFields
however, my goal is to have all Achievements / Actions in that view, so those from the Domain as well as those from the ActionFields…
in a view like this:
visual representation of that view: https://i.stack.imgur.com/W6x7M.png
How could i achieve that?
i have defined the following models:
visualized data model: https://i.stack.imgur.com/qP9gS.png
which is in code:
Domain:
class Domain < ApplicationRecord
belongs_to :editor, class_name: 'User', foreign_key: :editor_id
belongs_to :deputy, class_name: 'User', foreign_key: :deputy_id
belongs_to :solution_category
has_many :action_fields, dependent: :destroy
has_many :actions, as: :actionable
has_many :achievements, as: :achievementable
accepts_nested_attributes_for :action_fields, :achievements, :actions, allow_destroy: true
validates :name, :solution_category_id, presence: true
end
ActionField:
class ActionField < ApplicationRecord
belongs_to :domain
belongs_to :editor, class_name: 'User', foreign_key: :editor_id
belongs_to :deputy, class_name: 'User', foreign_key: :deputy_id
has_many :actions, as: :actionable
has_many :achievements, as: :achievementable
accepts_nested_attributes_for :achievements, :actions, allow_destroy: true
end
Achievement:
class Achievement < ApplicationRecord
belongs_to :achievementable, polymorphic: true
validates :name, presence: true
end
then there is my domains_controller (i removed the unneeded methods to make it shorter)
class DomainsController < ApplicationController
before_action :set_domain, except: [:new, :create, :index, :domain_index]
def show
@action_fields = @domain.action_fields
@actions = Action.where(actionable: [@domain, *@action_fields])
.where(visible: true)
.order(domain_rank: :asc, action_field_rank: :asc)
@achievements = Achievement.where(achievementable: [@domain, *@action_fields])
.where(visible: true)
.order(domain_rank: :asc, action_field_rank: :asc)
end
def edit
authorize @domain
@action_fields = @domain.action_fields
@actions = Action.where(actionable: [@domain, *@action_fields])
.order(domain_rank: :asc, action_field_rank: :asc)
@achievements = Achievement.where(achievementable: [@domain, *@action_fields])
.order(domain_rank: :asc, action_field_rank: :asc)
end
private
def domain_params
params.require(:domain).permit(
:name,
:editor_id,
:editor_name,
:deputy_id,
:deputy_name,
:adjust_solution,
:adjust_skills,
:adjust_organization,
:solution_category_id,
:achievements_attributes => [
:id, :name, :domain_rank, :visible, :additional_info, :media, :ready, :rollout, :_destroy
],
:actions_attributes => [
:id, :domain_rank, :visible, :due_date, :_destroy
],
:action_fields_attributes => [
:id, :name, :domain_weight, :domain_id, :_destroy
]
)
end
def set_domain
@domain = Domain.find(params[:id])
end
end
the edit form (only with achievements to keep it short):
<%= form_for @domain do |domain| %>
<%= render 'layouts/error_messages', object: @domain %>
<div class="small-12">
<h4 class="text-center"><%= t('domains.edit_form.domain_achievements') %></h4>
<table class="hover draggable" id="domain_achievements">
<thead>
<tr>
<th class="text-center" width="50"></th>
<th class="text-center"><%= t('action_fields.headers.achievement_headline') %></th>
<th class="text-center"><%= t('action_fields.headers.additional_info') %></th>
<th class="text-center" width="100"><%= t('defaults.misc.visible') %></th>
<th class="text-center" width="50"></th>
<th></th>
</tr>
</thead>
<tbody class="achievements">
<%= domain.fields_for :achievements, @achievements do |f| %>
<tr>
<td>
<%= f.hidden_field :_destroy %>
<i class="fa fa-trash-o remove_row" aria-hidden="true"></i>
</td>
<td><%= f.text_field :name %></td>
<td><%= f.text_field :additional_info %></td>
<td>
<div class="small switch text-center">
<%= f.check_box :visible, id: dom_id(f.object), class: 'switch-input' %>
<label class="switch-paddle" for="<%= dom_id(f.object) %>">
<span class="show-for-sr"><%= t('domains.edit_form.achievement_screenreader') %></span>
<span class="switch-active" aria-hidden="true"><%= t('defaults.misc.y') %></span>
<span class="switch-inactive" aria-hidden="true"><%= t('defaults.misc.n') %></span>
</label>
</div>
</td>
<td class="text-center draggable">
<i class="fa fa-arrows" aria-hidden="true"></i>
<%= f.hidden_field :domain_rank, class: 'domain_rank' %>
</td>
</tr>
<% end %>
</tbody>
<tfoot class="achievements">
<tr>
<td colspan="8" class="text-right"><%= link_to_add_fields t('domains.buttons.add_achievement'), domain, :achievements %></td>
</tr>
</tfoot>
</table>
</div>
<hr>
<...snip...>
<% end %>