表单的多选下拉框在 web 项目中比较常用且常见,所以快速构建多选下拉框是每个 Rails 全栈开发者必备的技能。
这篇文章总结使用 select2 前端插件和 Rails 内置的视图帮助方法 select 或者 select_tag 渲染 select html 标签和 form_for 构建表单一个小技巧。
引入select2
下载 select 最新版的 js 和 css 文件并对应添加到 app/assets/javascript 和 app/assets/stylesheet 目录下, 然后分别在 rails 项目的 application.js 和 application.css 中引入。
最后在指定的 select 标签中绑定即可
$('.specify_class').select2({
tags: true,
tokenSeparators: [',', ' ']
});
Rails select, select_tag 和 form_for, form_tag 的用法
select 的用法
文档给出的例子如下:
select(method, choices = nil, options = {}, html_options = {}, &block)
看看具体场景的使用例子。编辑文章页面,有文章对应的 tag ,可新增、删除或者选择已有的系统中已有的标签。
= form_for( @post, html: { class: "form" }) do |f|
......
= f.select :tag_list, @tags.collect { |t| [t.name,t.name] }, {}, { :multiple => "multiple" }
......
在 Rails 项目中,新建、编辑的表单一般抽出来放在单独的一个文件中。例如在这里新建、编辑文章我们需要操作的都是文章标题,内容以及标签,所以可表单可放在一个 _form.html.haml
文件中,以此 new.html.haml
和edit.html.haml
render 其即可。
form_for 的使用技巧
那么这里引出了一个题外话,我建议使用@post 构建 form_for 而不是 :post ,理由是什么呢?因为这样就减少了写 form_for 的 url 和 method 参数,如果 @post 对象是数据库存在的一个记录,则 form_for 的 url 隐式为 post_path(@post),method 为 put ,这样表单提交之后就能被 update 处理。反之如果 @post 对象不是已经存在的记录,则 form_for 的 url 为 post_path,method 为 post ,所以表单提交后会被 create 处理。
另外,表单中的 @post 和 @tags 对象建议在 _form.html.haml 文件中构建而不是在 new 或者 edit action 中构建传到页面,理由是当表单提交保存失败之后,update 或者 create action render 重新渲染页面对应页面不会因为没有请求对应的 new 或者 edit action 而导致这两个对象不存在。关于 render 的使用方法具体可以看 Rails render 和 redirect_to 进阶理解
form_tag 注意事项
首先看 API 的结构form_tag(url_for_options = {}, options = {}, &block)
options 有 multipart, method, authenticity_token, remote以及 html 属性。
通过new_record?
方法判断 @post 对象是否为数据库中存在的记录,form_for 直接使用 @post 对象之后能自动构建表单的 url 和 method ,但是在 form_tag 中,method 默认的 method 为 :post
,即使 @post 对象为数据库中存在的记录,但是 method 还是 :post
,如果是 update 操作,我们需要表单的 method 值为 :put
,此时就要显式地在 form_tag 方法中使用 method 参数,值为 :put
,例如:
- method_form = @member.new_record? ? :post : :put
= form_tag(url,method: "#{method_form}"
还有一点需要注意的是,如果通过浏览器审查元素,发现 form 元素的method 还是 post, 类似下面的这种情况:
这是为什么呢,翻阅 form_tag API 发现对 method 参数有一段描述
:method
- The method to use when submitting the form, usually either “get” or “post”. If “patch”, “put”, “delete”, or another verb is used, a hidden input with name _method
is added to simulate the verb over post.
然后不得而知 method 显式声明为 非 post 时,渲染页面时,表单会有一个隐藏 input
所以上文提到 form_for 使用小技巧就不再适用了,在 非 post 请求时,即使 @post 对象为数据库中存在的记录,我们还是需要显式地指定 form_tag 的 method 参数。
select 的选项
当 @post 是一个存在记录的对象,我们可以通过 @post.tag_list
拿到与该篇文章所有关联的 tags 对象数组。所以在编辑页面,f.select :tag_list
能渲染出已经关联的 tag 对象,我编辑的某篇文章有 test, list, javascript 标签,所以当编辑页面渲染时,下拉框渲染如下:
这部分只要看 choices = nil
和 html_options = {}
,前者是下拉框的选项,只要是 hash 即可,在这里通过 collect 方法构建了 hash k/v ;后者是 html 标签的属性,由于是多选框,所以我们添加了 multiple 属性。
select_tag 的用法
如果表单是通过 form_tag
构建的表单,那么 Rails 的 select 视图帮助方法就显得不行了,但是我们可以使用 select_tag
select_tag(name, option_tags = nil, options = {})
第二个参数是下拉框选项,第三个可选参数有如下:
- multiple : 如果值为 true,则为多选框(这里我们需要的改可选参数)
- disabled : 如果值为 true, 则改下拉框不可用状态
- include_blank : 如果值为 true ,则建立一个空的可选项,如果值为其他字符串,则新建的可选项的内容为该字符串,但为空值
- prompt : 其作用是提示,譬如:如果其值为 "选择用户"。
- 其他 html 属性
看一个例子(一个用户可以被指派到过个组织):
= select_tag :group, options_from_collection_for_select(@groups,"id","name", @selected_group), :multiple => true, class:'group_select'
上面代码中 :tag_list
并不像上部分 select 必须如此写,因为 f.select
通过 :tag_list
获取与 @post 对象关联的 tag ,但是在 select_tag
中,关联的 tag 对象只能在可选项中标明那个 tag 已经和 @post 对象关联了。那么如何在可选项中标明那些 tag 已经被选了呢。来看看 Rails 构建 select option 的视图帮助方法:
options_from_collection_for_select(collection, value_method, text_method, selected = nil)
结合例子中看看
options_from_collection_for_select(@groups,"id","name", @selected_group)
那么 edit action 渲染完成时,@selected_group
对象被渲染出来。
这里需要注意的是 value_method 和 selected 两个参数应一致。在这里 value_method 是组织记录的 id ,所以 selected 应是组织 id 构成的 ids 数组。我们继续贴一下 @selected_group 对象是怎么拿到的 @user.groups.pluck(:id)
,由于 user 和 group 是一对多关系,通过 pluck
拿到了该用户所属组织的 ids 。
总结
写到这里差不多结束了,总结一下:
- select2 前端插件的简单使用,一些高级用法可翻官方文档详细看看
- Rails 视图帮助方法
select
和select_tag
使用方法。 - Rails form_for, form_tag 帮助方法使用,建议使用
Resource-oriented style
构建表单,即就是使用 model 对象的方式。
详情可翻看官方文档的解释。Resource-oriented style 部分。