0

ユーザーが 5 つの性格特性を選択する Person というモデルがあります。ただし、彼らがそれらを選択する順序は重要です (彼らは最も説明的なものから最も説明的でないものへと選択しています)。

毒のある結合テーブルを作成し、そのように注文する方法を知っています。私もacts_as_listを使っています。

しかし、私のアプリのユーザーが特性の順序を設定する方法を作成する方法は、何の助けも見つからないようです。つまり、HTML に 5 つの選択ボックスを配置して、それぞれを選択させ、jQuery UI Sortable のようなものを使用して、必要に応じてそれらを移動できるようにしたいと考えています。

これが私のモデルの基本的な考え方です(概念を理解するために単純化されています)。

class Person < ActiveRecord::Base
  has_many :personalizations
  has_many :traits, :through => :personalizations, :order => 'personalizations.position'
end

class Personalization < ActiveRecord::Base
  belongs_to :person
  belongs_to :trait
end

class Trait  < ActiveRecord::Base
  has_many :persons
  has_many :persons, :through => :personalizations
end

ビュー/コントローラーでポジショニングを機能させる方法がわからないため、フォームを送信すると、どの特性がリストのどこにあるのかがわかります。

4

1 に答える 1

1

多くの調査の後、結果を投稿して、ビュー内の選択肢を並べ替えることができる関係を介して、多対多の関係を介してモデルにレコードのリストを添付する必要がある他の誰かを支援します。

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
于 2013-01-16T05:04:35.773 に答える