理解参数命名约定

使用 fields_for 帮助方法也可创建类似的绑定,但不会生成 <form> 标签。在同一表单中编辑多个模型对象时经常使用 fields_for 方法。例如,有个 Person 模型,和 ContactDetail 模型关联,编写如下的表单可以同时创建两个模型的对象:

<%= form_for @person , url: {action: "create" } do |person_form| %>
   <%= person_form.text_field :name %>
   <%= fields_for @person .contact_detail do |contact_details_form| %>
     <%= contact_details_form.text_field :phone_number %>
   <% end %>
<% end %>

生成的 HTML 如下:

< form accept-charset = "UTF-8" action = "/people/create" class = "new_person" id = "new_person" method = "post" >
   < input id = "person_name" name = "person[name]" type = "text" />
   < input id = "contact_detail_phone_number" name = "contact_detail[phone_number]" type = "text" />
</ form >

fields_for 方法拽入的对象和 form_for 方法一样,都是表单构造器(其实在代码内部 form_for会调用 fields_for 方法)。



从前几节可以看出,表单提交的数据可以直接保存在 params Hash 中,或者嵌套在子 Hash 中。例如,在 Person 模型对应控制器的 create 动作中,params[:person] 一般是一个 Hash,保存创建 Person 实例的所有属性。params Hash 中也可以保存数组,或由 Hash 组成的数组,等等。

HTML 表单基本上不能处理任何结构化数据,提交的只是由普通的字符串组成的键值对。在程序中使用的数组参数和 Hash 参数是通过 Rails 的参数命名约定生成的。

如果想快速试验本节中的示例,可以在控制台中直接调用 Rack 的参数解析器。例如: T> rubyTIP: Rack::Utils.parse_query "name=fred&phone=0123456789"TIP: # => {"name"=>"fred", "phone"=>"0123456789"}TIP:

7.1 基本结构

数组和 Hash 是两种基本结构。获取 Hash 中值的方法和 params 一样。如果表单中包含以下控件:

< input id = "person_name" name = "person[name]" type = "text" value = "Henry" />

得到的 params 值为:

{'person' => {'name' => 'Henry'}}

在控制器中可以使用 params[:person][:name] 获取提交的值。

Hash 可以随意嵌套,不限制层级,例如:

< input id = "person_address_city" name = "person[address][city]" type = "text" value = "New York" />

得到的 params 值为:

{ 'person' => { 'address' => { 'city' => 'New York' }}}

一般情况下 Rails 会忽略重复的参数名。如果参数名中包含空的方括号([]),Rails 会将其组建成一个数组。如果想让用户输入多个电话号码,在表单中可以这么做:

< input name = "person[phone_number][]" type = "text" />
< input name = "person[phone_number][]" type = "text" />
< input name = "person[phone_number][]" type = "text" />

得到的 params[:person][:phone_number] 就是一个数组。

7.2 结合在一起使用

上述命名约定可以结合起来使用,让 params 的某个元素值为数组(如前例),或者由 Hash 组成的数组。例如,使用下面的表单控件可以填写多个地址:

< input name = "addresses[][line1]" type = "text" />
< input name = "addresses[][line2]" type = "text" />
< input name = "addresses[][city]" type = "text" />

得到的 params[:addresses] 值是一个由 Hash 组成的数组,Hash 中的键包括 line1line2 和city。如果 Rails 发现输入框的 name 属性值已经存在于当前 Hash 中,就会新建一个 Hash。

不过有个限制,虽然 Hash 可以嵌套任意层级,但数组只能嵌套一层。如果需要嵌套多层数组,可以使用 Hash 实现。例如,如果想创建一个包含模型对象的数组,可以创建一个 Hash,以模型对象的 ID、数组索引或其他参数为键。

数组类型参数不能很好的在 check_box 帮助方法中使用。根据 HTML 规范,未选中的复选框不应该提交值。但是不管是否选中都提交值往往更便于处理。为此 check_box 方法额外创建了一个同名的隐藏 input 元素。如果没有选中复选框,只会提交隐藏 input 元素的值,如果选中则同时提交两个值,但复选框的值优先级更高。处理数组参数时重复提交相同的参数会让 Rails 迷惑,因为对 Rails 来说,见到重复的 input 值,就会创建一个新数组元素。所以更推荐使用 check_box_tag 方法,或者用 Hash 代替数组。

7.3 使用表单帮助方法

前面几节并没有使用 Rails 提供的表单帮助方法。你可以自己创建 input 元素的 name 属性,然后直接将其传递给 text_field_tag 等帮助方法。但是 Rails 提供了更高级的支持。本节介绍 form_for和 fields_for 方法的 name 参数以及 :index 选项。

你可能会想编写一个表单,其中有很多字段,用于编辑某人的所有地址。例如:

<%= form_for @person do |person_form| %>
   <%= person_form.text_field :name %>
   <% @person .addresses. each do |address| %>
     <%= person_form.fields_for address, index: address.id do |address_form| %>
       <%= address_form.text_field :city %>
     <% end %>
   <% end %>
<% end %>

假设这个人有两个地址,ID 分别为 23 和 45。那么上述代码生成的 HTML 如下:

< form accept-charset = "UTF-8" action = "/people/1" class = "edit_person" id = "edit_person_1" method = "post" >
   < input id = "person_name" name = "person[name]" type = "text" />
   < input id = "person_address_23_city" name = "person[address][23][city]" type = "text" />
   < input id = "person_address_45_city" name = "person[address][45][city]" type = "text" />
</ form >

得到的 params Hash 如下:

{ 'person' => { 'name' => 'Bob' , 'address' => { '23' => { 'city' => 'Paris' }, '45' => { 'city' => 'London' }}}}

Rails 之所以知道这些输入框中的值是 person Hash 的一部分,是因为我们在第一个表单构造器上调用了 fields_for 方法。指定 :index 选项的目的是告诉 Rails,其中的输入框 name 属性值不是 person[address][city],而要在 address 和 city 索引之间插入 :index 选项对应的值(放入方括号中)。这么做很有用,因为便于分辨要修改的 Address 记录是哪个。:index 选项的值可以是具有其他意义的数字、字符串,甚至是 nil(此时会新建一个数组参数)。

如果想创建更复杂的嵌套,可以指定 name 属性的第一部分(前例中的 person[address]):

<%= fields_for 'person[address][primary]' , address, index: address do |address_form| %>
   <%= address_form.text_field :city %>
<% end %>

生成的 HTML 如下:

< input id = "person_address_primary_1_city" name = "person[address][primary][1][city]" type = "text" value = "bologna" />

一般来说,最终得到的 name 属性值是 fields_for 或 form_for 方法的第一个参数加 :index 选项的值再加属性名。:index 选项也可直接传给 text_field 等帮助方法,但在表单构造器中指定可以避免代码重复。

为了简化句法,还可以不使用 :index 选项,直接在第一个参数后面加上 []。这么做和指定 index: address 选项的作用一样,因此下面这段代码

<%= fields_for 'person[address][primary][]' , address do |address_form| %>
   <%= address_form.text_field :city %>
<% end %>

生成的 HTML 和前面一样。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值