21

現在、深い入れ子のある複雑なフォームがあり、Cocoon gemを使用して、必要に応じてセクションを動的に追加しています(たとえば、ユーザーが販売フォームに別の車両を追加したい場合)。コードは次のようになります。

<%= sale.fields_for :sale_vehicles do |sale_vehicles_builder| %>
    <%= render :partial => "sale_vehicles/form", :locals => {:f => sale_vehicles_builder, :form_actions_visible => false} %>    
<% end -%>
<div class="add-field-links">
    <%= link_to_add_association '<i></i> Add Vehicle'.html_safe, sale, :sale_vehicles, :partial => 'sale_vehicles/form', :render_options => {:locals => {:form_actions_visible => 'false', :show_features => true, :fieldset_label => 'Vehicle Details'}}, :class => 'btn' %>
</div>

これは、ネストの最初のレベルで非常にうまく機能します。sale_vehicleオブジェクトはCocoonによって正しく構築され、フォームは期待どおりにレンダリングされます。

問題は、別のレベルのネストがある場合に発生します。sale_vehicle部分的なものは次のようになります。

<%= f.fields_for :vehicle do |vehicle_builder| %>
    <%= render :partial => "vehicles/form", :locals => {:f => vehicle_builder, :f_parent => f, :form_actions_visible => false, :show_features => true, :fieldset_label => 'Vehicle Details'} %>
<% end -%>

オブジェクトが作成されていないため、のパーシャルはvehicleフィールドなしでレンダリングされます。sale_vehicle.vehicle

したがって、私がする必要があるのは、メインオブジェクトと一緒にネストされたオブジェクトを構築することです(Cocoonは現在ネストされたオブジェクトを構築していません)が、これを行うにはどうすればよいですか?ヘルパーコードからネストされたフォームを選択して、これらをビルドできるようにする方法はありますか?

Cocoonは現在、次のようなメインオブジェクトを作成しています。

if  instance.collection?
    f.object.send(association).build
else
    f.object.send("build_#{association}")
end

次のようなことができれば、物事は素晴らしくシンプルに保たれますが、取得方法がわかりませんf.children-親フォームビルダーからネストされたフォームビルダーにアクセスする方法はありますか?

f.children.each do |child|
    child.object.build
end

これを機能させるために感謝するか、これらのオブジェクトを動的に構築する別の方法を提案してください。

ありがとう!

編集:おそらく、この質問は、上記のCocoon gemと、RyanBatesのnested_formgemの両方に関連しているように見えることを言及する価値があります。コクーンジェムの問題#91はこれと同じ問題のようですが、dnagir(オブジェクトの構築を委任する)によって提案された回避策は、他のフォームで問題を引き起こすため、この状況では理想的ではありません。

4

1 に答える 1

30

2番目のネストされたフォームにはがないことがわかりますlink_to_add_association

繭の内部でlink_to_add_associationは、ユーザーが動的に追加したい場合に備えて、新しい要素の構築を行います。

または、aが作成sale_vehicleされると、自動的に?が含まれる必要があることを意味していvehicleますか?ユーザーが販売されている車両を選択する必要があると思いますか?

二重にネストされたフォームを示すテストプロジェクトがあります。プロジェクトにはタスクがあり、サブタスクを持つことができます。

しかし、それはあなたがやりたいことと十分に関連していないのでしょうか?

あなたはあなたのモデルを見せませんが、私が正しく理解していれば、関係は

sale 
  has_many :sale_vehicles
sale_vehicle
  has_one :vehicle (has_many?)

したがって、を含めることsale_vehicleができるを持っている場合は、ユーザーが最初にをに追加し、次にリンクをクリックしてをvehicle追加すると想定します。それが繭が完璧にできることです。一方、繭が動的にを作成するときに、aも作成されるようにしたい場合は、いくつかの異なるオプションが表示されます。sale_vehiclesalevehiclesale_vehiclevehicle

使用するafter_initialize

私がこれの本当のファンであるとは言えませんが、after_initializeあなたのコールバックでは、sale_vehicleいつでも必要なvehicleモデルを構築できます。

sale_Vehicleここでは、モデルがないと有効ではない/存在できないためvehicle、ビルド直後にネストされたモデルを作成するのはモデルの責任であると想定しています。

これはオブジェクトの作成ごとafter_initializeに実行されるため、コストがかかる可能性があることに注意してください。しかし、これは簡単な修正になる可能性があります。空のネストされたモデルを拒否する場合、これはimhoで機能するはずです。

デコレータ/プレゼンターを使用する

ユーザーにとって、sale_vehiclevehicleは1つのオブジェクトのように見えるので、sale_vehicleと車両で構成されるデコレータを作成して1つの(ネストされた)フォームに表示し、これを保存するときに、デコレータは正しいものに保存する必要があることを認識します。モデル。

注:これにはさまざまな用語があります。デコレータは通常、いくつかのビューメソッドを使用して単一のクラスを拡張するだけですが、さまざまなモデルを組み合わせたものにすることもできます。代替用語:プレゼンター、ビューモデル。

とにかく、デコレータ/プレゼンターの機能は、ユーザーの基礎となるデータモデルを抽象化することです。したがって、何らかの理由で単一のエンティティを2つのデータベースモデルに分割する必要がありましたが(たとえば、列の数を制限したり、モデルを読みやすくしたりするためなど)、ユーザーにとっては単一のエンティティのままです。したがって、それを1つとして「提示」します。

build繭がカスタムメソッドを呼び出すことを許可する

私がこれのファンかどうかはわかりませんが、これは間違いなく可能性です。「ネストされたモデル」がActiveRecord::Associationでない場合はすでにサポートされているため、これを追加するのはそれほど難しくありません。しかし、私はその追加に躊躇しています。これらのオプションはすべて、それをより複雑にします。

編集:最も簡単な修正

パーシャルの中に、必要な子オブジェクトを作成するだけです。これは前に行わfields_forなければならず、それからあなたは行ってもいいです。何かのようなもの

<% f.object.build_vehicle %>
<%= f.fields_for :vehicle do |vehicle_builder| %>
    <%= render :partial => "vehicles/form", :locals => {:f => vehicle_builder, :f_parent => f, :form_actions_visible => false, :show_features => true, :fieldset_label => 'Vehicle Details'} %>
<% end -%>

結論

個人的にはデコレータのアプローチが本当に好きですが、少し重いかもしれません。を呼び出す前にオブジェクトをビルドするだけですfields_for。そうすれば、少なくとも1つは常に存在することがわかります。

私はあなたの考えを聞くことに興味があります。

お役に立てれば。

于 2012-10-05T23:31:30.053 に答える