I faced a similar situation. My solution may have performance issues in your scenario, this is not for me, but it is a low concurrency system, only 300 users.
What I did was with one kind / form of MEGA AJAX! and use javascript to show / hide if necessary. This method requires that you do not use form tag helpers (i.e. Text_field_tag instead of f.text_field), you must take control of the element names.
So, start creating a single view with all the forms that you ultimately need. You will have to distinguish between them, so put each in a div element with a unique identifier.
<%= form_for @mega, :remote=>true do %> <div id='main_part'> <%= render :partial => "main_part", :object=>@mega %> </div <div id='subpart1'> <%= render :partial => "subpart1" , :object=>@foobar , :locals=>{:id=>@foobar.id} %> </div <div id='subpart2'> <%= render :partial => "subpart2" , :object=>@barfoo , :locals=>{:id=>@barfoo.id} %> </div <% end %>
An example of partial parts of a form to distinguish between submit buttons:
<%= label_tag "main_part[name]" ,"Name" %> <%= text_field_tag "main_part[name]" , main_part.name %> <%= submit_tag "UPDATE", :name=>'main_part' %> <%= hidden_tag_field "subpart1_id" , id %> <%= label_tag "subpart1[city]" ,"City" %> <%= text_field_tag "subpart1[city]" , subpart1.city%> <%= submit_tag "ADD", :name=>'subpart1' %>
So, now you need a mega-controller, because this one form is sent to one controller action. This controller will look like a regular controller only for your top-level model, but for ALL models you will need to complete the household.
This controller action should determine which submit button is pressed, i.e.
def update if params[:main_part]
Since the mega form is remote => true, you need to create a javascript file that will reload all your partial parts of the form, so in app / views / megas / update.js.erb:
$('#main_part').html('<%= escape_javascript(render :partial=> "main_part", :object=>@mega) %>'); $('#subpart1').html('<%= escape_javascript(render :partial=> "subpart1", :object=>@foobar :locals=>{:id=>@foobar.id) %>');
Now, this is where performance issues arise. If you notice that if I run this javascript, it will expect all these instance variables to be defined, so the various partial ones will display update select tags that received new values as a result of the update. In my case, I just load them all into a filter before doing it, i.e.
class MegaController < ApplicationController before_filter :load, :only=>[:edit] def load @mega = Mega.find(params[:id]) @foobar = @mega.foobar @barfoo = @foobar.barfoo end
But you also could NOT do this, but instead create separate javascript files and render them specifically in the controller, i.e.
def update if params[:main_part] # do whatever... render :action=>'update_set1', :handler=>[:erb], :formats=>[:js] elsif params[:subpart1] # do whatever render :action=>'update_set1', :handler=>[:erb], :formats=>[:js] elsif params[:subpart2] # do whatever render :action=>'update_set2', :handler=>[:erb], :formats=>[:js] end end
The app / views / mega / update_set1.js.erb file will only update partial files affected by the upgrade to @mega or @foobar, update_set2.js.erb will update the partial files affected by the upgrade to @barfoo.
Last point, you are the form remote => true, how do you exit? Assuming you have:
<%= submit_tag 'Cancel', :name=>'cancel' %>
Then in the controller you will do something like:
def update if params[:cancel] render :js=> "window.location = '/'" else
The final step is to add javascript to show / hide the divs form as needed, an exercise left to the reader ....
UPDATE
class Mega < ActiveRecord::Base def self.get_param_name self.class.name end def self.get_id_name "#{self.class.name}_id" end end class MyModel < Mega end
then in the mega-controller:
def edit @mymodel = MyModel.find(params[MyModel.get_id_name]) end