Rails 3.2:嵌套表单演示,第二部分:加速攻击速度!





4.00/5 (1投票)
Rails 3.2:嵌套表单演示,第二部分
概述
在我们上一篇文章中,我们勇敢的英雄们正飞速驶入“星际战斗机识别指南”的战壕。他们操纵着应用程序的整体结构,在自己(Pilots
)和他们驾驶的Ships
之间建立了一种关系。在这篇文章中,我们王牌反抗军飞行员将着陆,进入我们的应用程序表面。
控制器
由于所有繁重的工作都由我们领域模型中定义的ActiveRecord配置处理,所以Ship
模型的控制器是相当标准的。快速回顾一下,ships_controller
中的create
方法看起来是这样的:
def create @ship = Ship.new(params[:ship]) if @ship.save redirect_to(ships_path, :notice => "The #{ @ship.name } ship has been saved successfully.") else render(:new, :error => @ship.errors) end end
如果您有兴趣查看整个控制器,可以在这里查看。
辅助方法
借鉴Railscast的灵感,我添加了几个辅助方法来让我的生活更轻松。
app/helpers/application_helper.rb
module ApplicationHelper def link_to_remove_fields(name, f, options = {}) f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)", options) end def link_to_add_fields(name, f, association, options = {}) new_object = f.object.class.reflect_on_association(association).klass.new fields = f.fields_for(association, new_object, :child_index => "new_#{ association }") do |builder| render(association.to_s.singularize + "_fields", :f => builder) end link_to_function(name, "add_fields(this, \"#{ association }\", \"#{ escape_javascript(fields) }\")", options) end end
上面的代码几乎是直接从Railscast复制过来的,但我仍然认为值得回顾一下。
link_to_remove_fields:
这个方法将创建一个隐藏的_destroy
字段(告诉我们记录是否应该被删除)和一个超链接,该链接将调用一个javascript方法来更新我们的_destroy
字段。link_to_add_fields:
这个方法将- 创建一个我们关联对象的新实例(在这种情况下是新的
Pilot
)。 - 构建一个我们可以用来编辑新
Pilot
对象的表单。 :child_index
将是一个占位符,将被javascript生成的唯一值替换(稍后会详细介绍)。- 构建一个包含我们
Pilot
对象的表单的超链接。
- 创建一个我们关联对象的新实例(在这种情况下是新的
fields_for
方法
让我们看看link_to_add_fields
的第二行和第三行(上面代码的第8行和第9行)发生了什么。我们正在使用fields_for
方法为我们构建Pilot
的输入字段。从宏观上看,fields_for
方法允许我们构建一个不带<form>
标签的HTML表单。这意味着我们可以毫无问题地将我们的字段放入“父”表单中。
fields_for
方法接受几个参数:
record_name
:我们要创建的记录类型的名称。在这种情况下,我们将为这个参数传递“Pilots”。record_object
:我们要添加/编辑的对象的一个实例。在这种情况下,它将是一个Pilot
对象。我们在link_to_add_fields
方法的上一行(上面代码的第7行)创建了一个新的Pilot
对象。options
:我们可以传递给fields_for
方法的任何选项。在这种情况下,我们希望有一种方法来唯一标识我们创建的每个Pilot
。因此,我们使用:child_index
,并将其设置为一个“占位符”。对于这个特定的例子,我们的占位符文本将是“new_pilots”。当表单显示时(即调用add_fields
javascript方法时),这个占位符将被一个唯一的标识符替换。
在我们的fields_for
块中,我们要求它渲染我们的_pilot_fields.html.erb
部分视图。注意第二个参数,:f => builder
是父表单(即包含我们正在处理的Ship
的输入字段的表单)。这意味着我们在调用fields_for
时渲染的Pilot
字段将是“添加飞船”表单的一部分,这正是我们想要的。
此时,我们的fields
变量应该看起来有点像这样(为了可读性而整理了一下)。
添加飞行员
<label for="ship_pilots_attributes_new_pilots_first_name" class="control-label">First name</label>
<input type="text" size="30" name="ship[pilots_attributes][new_pilots][first_name]" class="input-xlarge field" id="ship_pilots_attributes_new_pilots_first_name" />
<label for="ship_pilots_attributes_new_pilots_last_name" class="control-label">Last name</label>
<input type="text" size="30" name="ship[pilots_attributes][new_pilots][last_name]" class="input-xlarge field" id="ship_pilots_attributes_new_pilots_last_name" />
<label for="ship_pilots_attributes_new_pilots_call_sign" class="control-label">Call sign</label>
<input type="text" size="30" name="ship[pilots_attributes][new_pilots][call_sign]" class="input-xlarge field" id="ship_pilots_attributes_new_pilots_call_sign" />
在link_to_add_fields
方法的最后一行(上面代码的第12行),我们将fields
变量的内容传递给link_to_function
方法。link_to_function
方法正在设置一个链接,该链接将调用我们的add_fields
javascript方法(在下一节中描述)。fields
值将用作add_fields
方法的content
参数。
JavaScript
没有一点JavaScript的Web应用算什么?我们上面展示的辅助方法利用了Rails内置的link_to_function
辅助方法。link_to_function
将用于调用下面显示的JavaScript方法。
app/assets/javascripts/application.js
function remove_fields(link) { $(link).prev("input[type=hidden]").val("1"); $(link).closest(".fields").hide(); } function add_fields(link, association, content) { var new_id = new Date().getTime(); var regex = new RegExp("new_" + association, "g"); $(link).parent().after(content.replace(regex, new_id)); $('#new-pilot-fields').modal('show'); }
同样,上面显示的代码几乎是直接从Railscast复制过来的。remove_fields
函数相当直接。
- 找到位于我们“删除”链接(我们的
_destroy
字段)之前的隐藏输入字段,并将其值设置为1(即true
,意味着我们希望ActiveRecord为我们删除此记录)。 - 找到具有
class="fields"
的最近元素(在我们的例子中,这将是一个<TR>
元素)并将其隐藏。瞧!就用户界面而言,我们已经删除了一名Pilot
!
add_fields
函数稍微复杂一些,但没什么太花哨的。
- 基于当前时间生成一个任意的唯一ID。
- 构建一个正则表达式,用于搜索“new_”+[无论我们的关联名称是什么](在本例中为“new_pilots”)。
- 搜索我们的
content
(代表我们Pilot
表单的字符串),并将“new_pilots”替换为我们在步骤1中生成的唯一ID。 - 将
Pilot
表单添加到DOM。- 非常清楚的是,在
fields_for
方法描述中显示的HTML将被添加到我们的“添加飞船”屏幕(我们还没有看到,但请相信我,它就在路上)。
- 非常清楚的是,在
- 将
Pilot
表单显示为一个模态弹出窗口。modal
方法来自Twitter Bootstrap。
现在,当用户点击我们的“添加飞行员”链接(我们将在下一篇文章中看到)时,一个带有我们Pilot
输入字段的模态表单将出现。太棒了!
总结:少说废话,红色2!
我们正全速前进,护盾全开。虽然我们还没有开始对视图进行攻击,但我们的方法已经设置好了——这篇文章中的代码将使设置我们的视图变得容易得多。在下一期中,我们将打开我们的瞄准计算机,攻击视图。别看,我们的反抗军将拥有一个功能齐全的“星际战斗机识别指南”,用以记录他们的功绩和英勇事迹。
保持瞄准!