Reputation: 871
I have the following three models:
class Site < AR::Base
has_many :installed_templates, :as => :installed_templateable, :dependent => :destroy
accepts_nested_attributes_for :installed_templates, :allow_destroy => true, :update_only => true, :reject_if => lambda { |t| t[:template_id].nil? }
end
class Template < AR::Base
has_many :installed_templates, :inverse_of => :template
end
class InstalledTemplate < AR::Base
belongs_to :template, :inverse_of => :installed_template
belongs_to :installed_templateable, :polymorphic => true
end
The business logic is that several Templates
exist when I create a Site
and I can associate as many as I'd like by creating an InstalledTemplate
for each. A Site
can only have unique Templates
- I can't associate the same Template
twice to a single Site
.
I have the following on the form for Site
:
<% Template.all.each_with_index do |template| %>
<%= hidden_field_tag "site[installed_templates_attributes][][id]", Hash[@site.installed_templates.map { |i| [i.template_id, i.id] }][template.id] %>
<%= check_box_tag "site[installed_templates_attributes][]]template_id]", template.id, (@site.installed_templates.map(&:template_id).include?(template.id) %>
<%= label_tag "template_#{template.id}", template.name %>
<% end %>
The above is the only thing that seems to work after lots of experimentation. I wasn't able to achieve this at all using the form_for
and fields_for
helpers.
It seems like a pretty straight forward relationship and I'm afraid I'm missing something. Anyone have advice on how to accomplish the above in a cleaner fashion?
Thanks
Upvotes: 1
Views: 170
Reputation: 1539
Try the following
<% form_for @site do |f| %>
<%f.fields_for :installed_templates do |af|%>
<%= af.text_field :name %>
<%end%>
<%end%>
Upvotes: 3
Reputation: 76
I think you are trying to do two different things here.
I think I would handle this through an accessor on the model.
On the Site Model (assuming standard rails naming conventions)
attr_accessor :template_ids
def template_ids
installed_templates.collect{|i_t| i_t.template.id}
end
def template_ids=(ids, *args)
ids.each{|i| i.installed_template.build(:site_id => self.id, :template_id => i) }
end
Then the form becomes pretty simple
<% form_for @site do |f| %>
<% f.collection_select :template_ids, Template.all, :id, :name, {}, :multiple => true %>
<% end %>
Upvotes: 1
Reputation: 5518
Is a join model necessary here?
class Site < ActiveRecord::Base
has_and_belongs_to_many :templates
end
In the form, easiest if using simple_form:
<%= form.input :template_ids, :as => :radio_buttons, :collection => Template.order(:name), :label => 'Templates installed:' %>
If the join model is necessary, then I would have a dropdown or list of templates I could add, each with a button that submits the form and adds that template. Then I'd use the update_only nested attributes form to display the currently installed templates with their settings.
class Site < ActiveRecord::Base
...
attr_accessor :install_template_id
before_validation :check_for_newly_installed_template
def check_for_newly_installed_template
if install_template_id.present?
template = Template.find install_template_id
if template.present? and not templates.include?(template)
self.templates << template
end
end
end
end
That works kind of like Drupal, where you have to enable the theme before you can edit its settings or select it as the current theme.
Upvotes: 1