views:

80

answers:

2

This are my models:

class Speaker < ActiveRecord::Base
  belongs_to :session, :foreign_key => :session_id, :class_name => :Session
  belongs_to :speakable, :polymorphic => true
end  

class Session < ActiveRecord::Base
  has_many :speakers
  accepts_nested_attributes_for :speakers
end

class Person < ActiveRecord::Base
  has_many :speakers, :as => :speakable
end       

class Company < ActiveRecord::Base
  has_many :speakers, :as => :speakable
end  

What I want to do now is something like this: app/views/sessions/edit.html.erb

  <% f.fields_for :speakers do |sf| %>
    <p>
      <%= sf.label :speaker %><br />
      <%= sf.collection_select :speakable, Company.all + Person.all, :id, :full_name %>  
    </p>
  <% end %>

But it is not working because of the polymorphic assignment. How do I approach this problem?

EDIT: The Error is:

undefined method `base_class' for String:Class

with params being:

"speaker"=>{"speakable"=>"1063885469", "session_id"=>"1007692731"}

The value passed to speakable is the id of the Speaker/Company. Yes, this is the value I specified the collection_select to return, but how can I manage to supply both values (speakable_id and speakable_type) ?

A: 

You can use a hidden_field to save the speakable_type. This way you'll have to change the value of the hidden_field every time the selected option changes. I'm not sure this is the best approach, but works...

Hope it helps you.

Edit

You'll have to define the :onchange option in your select.

<%= sf.collection_select :speakable, Company.all + Person.all, :id, 
   :full_name, {}, {:onchange => "setSpeakableType()"} %>  

The hidden_field starts with nil value.

<%= sf.hidden_field :speakable_type, :value => nil %>

And your setSpeakableType() function will have to set the proper speakable_type.

j.
Thank you for your answer. How do I set the value of the hidden field, since the speakable still has to be selected?
muxe
Edited my answer :]
j.
I am not very good at JS, so I will check out the suggestion made by jamuraa, but I think your approach should work.
muxe
A: 

Two ways I've solved this.

The first is to munge your collection in the form, so that you can figure out which type the id is to begin with. Something similar to this:

<% f.fields_for :speakers do |sf| %>

<%= sf.label :speaker %>
<% speakable_collection = Person.all.map {|x| [x.name, "Person:#{x.id}"]} + Company.all.map {|x| [x.name, "Company:#{x.id}"]} %> <%= sf.collection_select :speakable, speakable_collection, :id, :full_name %>

<% end %>

And then in your controller action:

def action
  if params[:speaker][:speakable].begins_with?("Person:")
     speak_type = 'Person'
     speak_id = params[:speaker][:speakable].split(":")[1].to_i
  elsif params[:speaker][:speakable].begins_with?("Company:")
     speak_type = 'Company'
     speak_id = params[:speaker][:speakable].split(":")[1].to_i
  end
  params[:speaker].delete(:speakable)
  obj = Speaker.new(params[:speaker])
  obj.speaker_type = speak_type
  obj.speaker_id = speak_id
  ... rest of action ... 
end

The second is to use javascript to change the hidden fields speakable_type and speakable_id which are hidden in the page. I ended up using an autocompleter to fill out this field, so invoking javascript when a selection was made was easy enough. The second way makes the controller a lot cleaner.

jamuraa
Thank you very much, this should work, even though it is very hackish :-)
muxe