多くの調査の後、結果を投稿して、ビュー内の選択肢を並べ替えることができる関係を介して、多対多の関係を介してモデルにレコードのリストを添付する必要がある他の誰かを支援します。
Ryan Bates は、既存のレコードの並べ替えに関する素晴らしいスクリーンキャストを公開しています: http://railscasts.com/episodes/147-sortable-lists-revised
ただし、私の場合、 Person モデルが存在する前に並べ替えを行う必要がありました。
builder または simple_form_for を使用して関連付けフィールドを簡単に追加でき、これがさらに簡単になります。結果は、各関連フィールドの属性 trait_ids を含む params になります (私の Person has_many Traits のため):
#view code (very basic example)
<%= simple_form_for @character do |f| %>
<%= (1..5).each do |i| %>
<%= f.association :traits %>
<% end %>
<% end %>
#yaml debug output
trait_ids:
- ''
- '1'
- ''
- '2'
- ''
- '3'
- ''
- '4'
- ''
- '5'
問題は、フォームが送信されるたびに、DOM 内の要素の順序が尊重されるかどうかです。特にjQuery UIドラッグ可能を実装する場合は? これが見つかりました 投稿フォームのデータの順序は、Webフォームのそれと同じですか? そして私は答えに同意します。私が思ったように、順序が常に保持されると仮定するのはリスクが高すぎます。現在すべてのブラウザで動作していても、バグが発生する可能性があります。
したがって、よく調べた結果、jQuery が優れたソリューションであると結論付けました。カスタム出力を処理するレールの仮想属性とともに。多くのテストの後、私がやろうとしていることにacts_as_listを使用することをあきらめました。
この投稿されたソリューションを少し説明します。基本的に、仮想プロパティへの変更をキャッシュします。次に、そのキャッシュが設定されている (変更が行われている) 場合、5 つの特性が選択されていることを確認します。私の目的のために、無効/nullの選択を保持しているので、ビューに戻ったときに検証が失敗した場合、順序は同じままになります(たとえば、中央の選択ボックスをスキップした場合)。
その後、after_save 呼び出しによって、これらの変更がデータベースに追加されます。after_save のエラーは引き続きトランザクションにラップされるため、いずれかの部分でエラーが発生した場合、変更は行われません。したがって、すべてのエンダウメントを削除して新しいものを保存するのが最も簡単でした (ここでより良い選択があるかもしれませんが、確かではありません)。
class Person < ActiveRecord::Base
attr_accessible :name, :ordered_traits
has_many :endowments
has_many :traits, :through => :endowments, :order => "endowments.position"
validate :verify_list_of_traits
after_save :save_endowments
def verify_list_of_traits
return true if @trait_cache.nil?
check_list = @trait_cache.compact
if check_list.nil? or check_list.size != 5
errors.add(:ordered_traits, 'must select five traits')
elsif check_list.uniq{|trait| trait.id}.size != 5
errors.add(:ordered_traits, 'traits must be unique')
end
end
def ordered_traits
list = @trait_cache unless @trait_cache.nil?
list ||= self.traits
#preserve the nil (invalid) values with '-1' placeholders
list.map {|trait| trait.nil?? '-1' : trait.id }.join(",")
end
def ordered_traits=(val)
@trait_cache = ids.split(',').map { |id| Trait.find_by_id(id) }
end
def save_endowments
return if @trait_cache.nil?
self.endowments.each { |t| t.destroy }
i = 1
for new_trait in @trait_cache
self.endowments.create!(:trait => new_trait, :position => i)
i += 1
end
end
次に、単純なフォームで隠しフィールドを追加します
<%= f.hidden :ordered_traits %>
jQuery を使用して、作成した 5 つの選択ボックスの div 内の正しい場所にエラー スパンとヒント スパンを移動します。次に、フォームに送信イベント ハンドラーを配置し、5 つのテキスト ボックスの選択内容を DOM 内の順序でコンマ区切りの数値の配列に変換し、非表示フィールドに値を設定しました。
完全を期すために、他のクラスを次に示します。
class Trait < ActiveRecord::Base
attr_accessible :title
has_many :endowments
has_many :people, :through => :endowments
end
class Endowment < ActiveRecord::Base
attr_accessible :person, :trait, :position
belongs_to :person
belongs_to :trait
end