我遇到了类似的情况。我的解决方案在您的场景中可能存在性能问题,它不适合我,但它是一个低并发使用系统,总共只有 300 个用户。
我所做的是使用一个 MEGA AJAX 视图/表单!并根据需要使用 JavaScript 显示/隐藏。此技术要求您不要使用表单标签助手(即 text_field_tag 而不是 f.text_field),您必须控制元素名称。
因此,开始创建一个包含您最终需要的所有表单的单一视图。您必须区分它们,因此将每一个都放在具有唯一 ID 的 div 元素中。
<%= 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 %>
表单部分示例,注意区分提交按钮:
<%= 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' %>
所以现在你需要一个大型控制器,因为这个表单发布到单个控制器操作。这个控制器看起来像一个普通的控制器,只适用于你的顶级模型,但它需要为所有模型做家务。
该控制器操作必须确定单击了哪个提交按钮,即
def update
if params[:main_part]
# since I controlled the parameter naming I know what's in params[:main_part]
# which is main_part[:name]
@mega = MainThing.find(params[:id])
@mega.attributes = params[:main_part]
@mega.save
# only for main_part is the id valid, in every other case you have to
# manually extract the id
elsif params[:subpart1]
@subpart1_id = params[:subpart1_id]
@foobar = FooBar.find(@subpart1_id)
@foobar.attrubutes = params[:subpart1]
@foobar.save
else
end
end
由于 mega 表单是 remote=>true,因此您需要创建一个 javascript 文件来重新加载所有表单部分,因此在 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) %>');
现在这是性能问题的来源。如果您注意到,如果我运行该 javascript,它将期望定义所有这些实例变量,因此各种部分将呈现刷新任何由于更新而获得新值的选择标签. 在我的情况下,我只是将它们全部加载到之前的过滤器中,即
class MegaController < ApplicationController
before_filter :load, :only=>[:edit]
def load
@mega = Mega.find(params[:id])
@foobar = @mega.foobar
@barfoo = @foobar.barfoo
end
但是您也可以不这样做,而是创建单独的 javascript 文件并专门在控制器中呈现它们,即
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
文件 app/views/mega/update_set1.js.erb 只会更新受@mega 或@foobar 更新影响的部分,update_set2.js.erb 将更新受@barfoo 更新影响的部分。
最后一点,你的表单是远程=>真,你怎么退出?假设你有:
<%= submit_tag 'Cancel', :name=>'cancel' %>
然后在控制器中,您将执行以下操作:
def update
if params[:cancel]
render :js=> "window.location = '/'"
else
# whatever....
end
end
最后一步是添加 javascript 以根据需要显示/隐藏表单 div,这是留给读者的练习....
更新
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
然后在一个大型控制器中:
def edit
@mymodel = MyModel.find(params[MyModel.get_id_name])
end