4

概述/型号

例如,假设您有一个相当大的系统,其中包含非常通用的模型(我们将在此示例中使用三个)

  • 位置(通用地址字段、类型、line1、line2、towncity 等)
  • 公司(通用公司字段、类型、名称等)
  • 联系人(通用联系人字段、类型、姓名、职位等)

示例(+ rails_admin 屏幕截图)

我发现在制作这样的系统时,我总是遇到同样的问题。例如,我单击公司上的添加按钮(所以我现在已经加载了部分表单等),我在添加公司的过程中发现我需要的位置不在系统中。我想快速打开一个模式窗口,添加位置,然后通过 jquery 或类似的方式更新选择。太好了,没有什么太难的了,它已经在 rails_admin 这样的系统中完成了(见下面的截图):

http://www.server1-breakfrom.com/nestedaddexample.jpg

当处理一层嵌套时,这一切都很好,一般来说,当处理这种情况时,一切都很好(因为你可以在能力中编程)。但是,我正在做一个系统,它本身几乎需要成为一个框架,因为我在超过 50% 的模型中都需要它。我需要能够在模型/控制器中动态添加各种​​选项并让表单动态生成相关按钮。

其他问题

  1. 模式内部/顶部的模式 - 添加联系人时,单击以添加他们的公司,在此新公司添加表单中,然后您要添加位置:繁荣,模式顶部的模式。
  2. jquery 附加到选择的文本 - 您需要知道如何更新选择元素,并可能在屏幕上找到所有相关的选择元素。添加公司可能会使用其 ID 和名称,但位置可能需要使用 ID 以及 line1、line2 和 towncity 作为文本。
  3. 模态中的验证(但我假设我们可以使用某种 jquery,因为我们已经非常依赖它)

扩大问题

所以,除了这个问题:我是否把事情复杂化了,是否有一个容易解决的问题(我必须强调这 3 个模型纯粹是例子,我有很多模型需要参考一家公司,这不仅仅是一个联系人,所以“只需根据需要对其进行编程”,将无法正常工作!)。

或者,我应该只是拆开 rails_admin 并取出我需要的位吗?(同样,他们还没有解决多重嵌套问题,所以我觉得从头开始可能会更好?)。

4

2 回答 2

2

我遇到了类似的情况。我的解决方案在您的场景中可能存在性能问题,它不适合我,但它是一个低并发使用系统,总共只有 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
于 2013-02-24T19:50:21.203 回答
1

解决问题

经过大量测试并仔细阅读了 RadBrad 提交的代码后,我偶然发现了一个 Gem,其答案几乎完全符合我对问题/短语“动态嵌套表单(使用 jQuery)”的期望。对于原始问题,“无限”似乎是一个糟糕的选择,但希望这个文本能让这个问题在搜索引擎上得到更好的选择。

宝石

该宝石被称为茧,可以在这里找到:https ://github.com/nathanvda/cocoon

附加功能

作为奖励,它是使用 jQuery 构建的,与 simple_form、formtastic 兼容,并且非常适合 Twitter Bootstrap!

补充笔记

我还没有完全测试它,而且说实话,验证看起来已经是一个小问题,但如果我发现任何不能按预期工作的东西,我会进一步更新这个答案。

Cocoon 提供的示例 github 项目已经过时,您需要在 Gem 文件中进行大量重新调整才能使它们工作(但它是可行的!)。请放心,主要 Gem(在撰写本文时)是最新的。

于 2013-02-26T21:07:40.467 回答