概述/模型
例如,假设您有一个相当大的系统,其中包含非常通用的模型(我们将在此示例中使用三个)
示例(+ rails_admin 截图)
我发现在制作这样的系统时,我总是遇到同样的问题。例如,我单击了公司上的添加按钮(所以我现在已经加载了部分表单等),我在添加公司的过程中发现我需要的位置不在系统中。我想快速打开一个模态窗口,添加位置,然后通过 jquery 或类似的东西更新选择。太棒了,没什么太难的,它已经在 rails_admin 之类的系统中完成了(见下面的截图):
http://www.server1-breakfrom.com/nestedaddexample.jpg
在处理一层嵌套时,这一切都很好,一般来说,将这种情况作为稀有情况处理时,一切都很好(因为您可以随时在能力中进行编程)。但是,我正在做一个系统,它本身几乎需要成为一个框架,因为我在超过 50% 的模型中都需要它。我需要能够在模型/ Controller 中动态添加各种选项,并让表单动态生成相关按钮。
其他问题
扩展问题
所以,除了问题:我是不是把事情复杂化了,有没有容易解决的问题(我必须强调这 3 个模型纯粹是示例,例如我有很多模型需要引用一家公司,这不仅仅是一个联系人,因此“只需根据需要对其进行编程”,就行不通了!)。
或者,我应该拆开 rails_admin 并取出我需要的部分吗? (同样,他们还没有解决多重嵌套问题,所以我觉得从头开始可能会更好?)。
最佳答案
我遇到了类似的情况。我的解决方案可能在您的场景中存在性能问题,但对我来说没有,但它是一个低并发使用系统,只有 300 个总用户。
我所做的是使用一个 MEGA AJAX View /表单!并根据需要使用 JavaScript 显示/隐藏。这种技术要求您不要使用表单标签助手(即 text_field_tag 而不是 f.text_field),您必须控制元素名称。
因此,开始使用您最终需要的所有表单创建一个 View 。您必须区分它们,因此将每个放在具有唯一 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' %>
所以现在你需要一个大型 Controller ,因为这个表单发布到单个 Controller 操作。这个 Controller 看起来只是你的顶级模型的常规 Controller ,但它需要为所有模型做家务。
该 Controller 操作必须确定单击了哪个提交按钮,即
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
由于大型表单是 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 文件并在 Controller 中专门呈现它们,即
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 更新影响的部分。
最后一点,你的表单是remote=>true,你如何退出?假设你有:
<%= submit_tag 'Cancel', :name=>'cancel' %>
然后在 Controller 中你会做这样的事情:
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
然后在大型 Controller 中:
def edit
@mymodel = MyModel.find(params[MyModel.get_id_name])
end