How can I elegantly construct a form for a model t

2019-04-10 15:27发布

Here are my models:

class Lesson < ActiveRecord::Base
  belongs_to :topic, :polymorphic => true  
  validates_presence_of :topic_type, :topic_id  
end

class Subject < ActiveRecord::Base  
   has_many :lessons, :as => :topic  
end

class Category < ActiveRecord::Base  
  has_many :lessons, :as => :topic  
end

Now, what I need is a form that will allow the user to create or update Lessons. The questions is, how can I provide a select menu that offers a mix of Subjects and Categories? (To the user, on this particular form, Subjects and Categories are interchangeable, but that's not the case elsewhere.)

Ideally, this would look something like this:

views/lessons/_form.html.haml

= simple_form_for(@lesson) do |f|
  = f.input :title
  = f.association :topic, :collection => (@subjects + @categories)

That won't work because we'd only be specifying the topic_id, and we need the topic_types as well. But how can we specify those values?

I guess the crux of the problem is that I really want a single select menu that specifies two values corresponding to two different attributes (topic_id and topic_type). Is there any elegant railsy way to do this?

A few notes:

a) Single table inheritance would make this issue go away, but I'd like to avoid this, as Categories and Subjects have their own relationship… I'll spare you the details.

b) I might could pull some javascript shenanigans, yes? But that sounds messy, and if there's a cleaner way to do it, some magic form helper or something, then that's certainly preferable.

c) Though I'm using simple_form, I'm not wedded to it, in case that's complicating matters.

Thanks

2条回答
虎瘦雄心在
2楼-- · 2019-04-10 15:51

If you don't wish to use STI, you can do something similar: create a new model Topic(name:string) which will polymorphically reference Subject or Category.

class Lesson < ActiveRecord::Base
  belongs_to :topic
  validates_presence_of :topic_id  
end

class Topic < ActiveRecord::Base
  belongs_to :topicable, :polymorphic => true
end

class Subject < ActiveRecord::Base
   has_one :topic, :as => :topicable
   has_many :lessons, :through => :topic
   accepts_nested_attributes_for :topic
end

class Category < ActiveRecord::Base  
   has_one :topic, :as => :topicable
   has_many :lessons, :through => :topic
   accepts_nested_attributes_for :topic
end

In the view where you create a new Subject/Category:

<%= form_for @subject do |subject_form| %>
  <%= subject_form.fields_for :topic do |topic_fields| %>
    <%= topic_fields.text_field :name %>
  <% end %>
<% end %>
查看更多
看我几分像从前
3楼-- · 2019-04-10 15:51

After thinking this through, the less dirty implementation IMO would be to hire the JS shenanigans (b):

= f.input_field :topic_type, as: :hidden, class: 'topic_type'
- (@subjects + @categories).each do |topic|
  = f.radio_button :topic_id, topic.id, {:'data-type' => topic.class.name, class: 'topic_id'}

With a sprinkle of JS (your needs may vary):

$('input:radio.topic_id').change(function() {
  $('input:hidden.topic_type').val($(this).attr('data-type'));
});

Notes:

  • I use a radio button to select a topic (category or subject) in a list
  • The class name of each of possible topic is stored in an attribute 'data-type'
  • When a radio button is selected, the class name is copied to the hidden input via JS
  • Using: HTML5, jQuery, haml, simple_form
查看更多
登录 后发表回答