I have a form that enables a user to update multiple alert rules records at the same time. But each alert rule record can have many notification emails. So I would like to be able to update alert rules and the associated notification emails within the same form.
Models:
class AlertRule < ActiveRecord::Base
has_many :notification_emails, :dependent => :destroy
accepts_nested_attributes_for :notification_emails, :reject_if => lambda { |notification| notification[:email].blank? }
end
class NotificationEmail < ActiveRecord::Base
belongs_to :alert_rule
end
In one of my controllers, I send an array of alerts to a form:
def alerts_config
//more code
@alert_rules = alert_rules.flatten
@alert_rules.each { |a| a.notification_emails.build }
render :partial => 'home/alerts_config', :layout => false
else
Then in my form, I want to allow the user to update alert rules and the associated email notifications:
= form_for @alert_rules, :url => '/home/save_alerts_config/' + @unit.id.to_s, :remote => true, :class => 'ajaxForm' do |f|
%table.scrollTable{:cellspacing => "0", :width => "740px", :style => "border-collapse: collapse; border-spacing: 0;"}
%thead.fixedHeader
%tr
%th Alerts
%th Enable
%th Primary Email
%th Notification Emails
%th
%tbody.scrollContent
- for rule in @alert_rules
%tr
%td= rule.alert_code.name
%td= check_box_tag "enabled_ids[]", rule.id
%td= f.text_field :email, :value => rule.email, :index => rule.id
%td.fields
= f.fields_for :notification_emails, rule.notification_emails do |notification_builder|
= notification_builder.text_field :email
= notification_builder.hidden_field :_destroy
= link_to_function 'Remove Notification', 'remove_notifications(this)'
.save_panel
= submit_tag "Save", :class => 'submit myButton'
When I submit to server, this is what I get:
Started POST "/home/save_alerts_config/6243" for 127.0.0.1 at 2012-03-20 17:49:06 -0400
Processing by HomeController#save_alerts_config as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"NPwuKuWippYjm2tJcfQI+/x9oEBwcR2rxcfpZMTO/Qo=", "enabled_ids"=>["51"], "alert_rule"=>{"51"=>{"email"=>"hythy"}, "notification_emails_attributes"=>{"0"=>{"email"=>"rtyrytry", "_destroy"=>"false"}, "1"=>{"email"=>"", "_destroy"=>"false"}, "2"=>{"email"=>"", "_destroy"=>"false"}, "3"=>{"email"=>"", "_destroy"=>"false"}, "4"=>{"email"=>"", "_destroy"=>"false"}, "5"=>{"email"=>"", "_destroy"=>"false"}, "6"=>{"email"=>"", "_destroy"=>"false"}, "7"=>{"email"=>"", "_destroy"=>"false"}, "8"=>{"email"=>"", "_destroy"=>"false"}, "9"=>{"email"=>"", "_destroy"=>"false"}, "10"=>{"email"=>"", "_destroy"=>"false"}, "11"=>{"email"=>"", "_destroy"=>"false"}, "12"=>{"email"=>"", "_destroy"=>"false"}, "13"=>{"email"=>"", "_destroy"=>"false"}, "14"=>{"email"=>"", "_destroy"=>"false"}, "15"=>{"email"=>"", "_destroy"=>"false"}, "16"=>{"email"=>"", "_destroy"=>"false"}, "17"=>{"email"=>"", "_destroy"=>"false"}, "18"=>{"email"=>"", "_destroy"=>"false"}, "19"=>{"email"=>"", "_destroy"=>"false"}, "20"=>{"email"=>"", "_destroy"=>"false"}, "21"=>{"email"=>"", "_destroy"=>"false"}, "22"=>{"email"=>"", "_destroy"=>"false"}, "23"=>{"email"=>"", "_destroy"=>"false"}, "24"=>{"email"=>"", "_destroy"=>"false"}, "25"=>{"email"=>"", "_destroy"=>"false"}, "26"=>{"email"=>"", "_destroy"=>"false"}, "27"=>{"email"=>"", "_destroy"=>"false"}, "28"=>{"email"=>"", "_destroy"=>"false"}, "29"=>{"email"=>"", "_destroy"=>"false"}, "30"=>{"email"=>"", "_destroy"=>"false"}, "31"=>{"email"=>"", "_destroy"=>"false"}, "32"=>{"email"=>"", "_destroy"=>"false"}, "33"=>{"email"=>"", "_destroy"=>"false"}, "34"=>{"email"=>"", "_destroy"=>"false"}, "35"=>{"email"=>"", "_destroy"=>"false"}, "36"=>{"email"=>"", "_destroy"=>"false"}, "37"=>{"email"=>"", "_destroy"=>"false"}, "38"=>{"email"=>"", "_destroy"=>"false"}, "39"=>{"email"=>"", "_destroy"=>"false"}, "40"=>{"email"=>"", "_destroy"=>"false"}}, "52"=>{"email"=>"yutu"}, "53"=>{"email"=>"ytuytu"}, "54"=>{"email"=>""}, "55"=>{"email"=>""}, "56"=>{"email"=>""}, "57"=>{"email"=>""}, "58"=>{"email"=>""}, "59"=>{"email"=>""}, "60"=>{"email"=>""}, "61"=>{"email"=>""}, "62"=>{"email"=>""}, "63"=>{"email"=>""}, "64"=>{"email"=>""}, "65"=>{"email"=>""}, "66"=>{"email"=>""}, "67"=>{"email"=>""}, "68"=>{"email"=>""}, "69"=>{"email"=>""}, "70"=>{"email"=>""}, "71"=>{"email"=>""}, "72"=>{"email"=>""}, "73"=>{"email"=>""}, "74"=>{"email"=>""}, "75"=>{"email"=>""}, "76"=>{"email"=>""}, "77"=>{"email"=>""}, "78"=>{"email"=>""}, "79"=>{"email"=>""}, "80"=>{"email"=>""}, "81"=>{"email"=>""}, "82"=>{"email"=>""}, "83"=>{"email"=>""}, "84"=>{"email"=>""}, "85"=>{"email"=>""}, "86"=>{"email"=>""}, "87"=>{"email"=>""}, "88"=>{"email"=>""}, "89"=>{"email"=>""}, "90"=>{"email"=>""}, "91"=>{"email"=>""}}, "commit"=>"Save", "id"=>"6243"}
This shouldnt be. As I show in the controller above, I only build one notification per alert, so why it sends all the notifications back as if they are all associated with the first alert, when I check the first alert only, is beyond me.
update:
even when I used create! instead of build to actually write the associated record, it still presented same problem: unable to get the alert id inside the name attribute of the notification_email input field
Thanks for response
You’re not going to like it. But this is what I did to make it work…
Information based on this stackoverflow question and was heavily influenced by this pretty awesome answer I can up with this:
models/alert_rules_set.rb
Controller
Form
Here’s a link to a git branch where I got it working with another app and the commit compare.
This is crazy! What’s going on?
So historically ActionPack tended to be pretty tightly coupled to ActiveRecord. This isn’t a good idea for several reasons, so starting with Rails 3.0 ActiveModel was introduced. Unfortunately this is one of those cases that could probably be better.
ActiveModel
NOTE: Some of this is changing, specifically with this Rails 4 commit
So basically, I’m implementing the bare minimum to make this case work. Apart from the
extendandincludedirectives, the only thing that is necessary to make any other ActiveModel class play nice in this case seems to bepersisted?.As for everything else…
attr_accessor :alert_rulesThis is just holding the
alert_rulesdata. This is whatfields_foris going to use to populate the fields with existing data.alert_rules_attributes=(attributes)This is kinda special. This is what
fields_forchecks for when it wants to know when to render a collection or not. Without this you will just get a single nested field(ie.[alert_rule][notification_emails_attributes]) instead of a collection of nested fields(ie.[alert_rule][notification_emails_attributes][1]).self.alert_rules_from(collection_set_hash)This is the more confusing part. So basically when you submit some nested attributes they’re actually in a
Hashwith an index key.This method is a quick way to get rid of that and get usable data out (This happens in ActiveRecord too). I figured it would probably be easier to manipulate the data than try to do some saving magic in the
AlertRuleSetclass (but you could).