前一天,我们使用了simple_form_for
提到了新增表单写法,而今天要讲一个上传情境。这个上传想要做的事情在特殊新增
页签中新增或更新id=1,2,3,4
的资料,上传後转址回首页,并且存在的的id: 1-4
的值会被填写在表单上面。
form_for
/ simple_form_for
的情境为一般crud
上传,而上述情境的特殊上传并不是遵照惯例的上传法,所以我们用form_tag
的方式,写出我们想要的功能。这里讲一下 form_for
/ simple_form_for
/ form_tag
的差别
Rails
内建的表单结构,适合写新增、更新单笔资料用。Rails
提供的helper
,但使用方式为普通的form
。写法比较自由,但写的行数比较多simple_form
,并且做一些基本的设定。这是基於 form_for 所延伸的更简易的用法,是我爱用的用法以下为关於 form_tag
的雷:
#====== 错误用法
form_tag(controller: "people", action: "search", method: "get", class: "nifty_form")
#=> '<form accept-charset="UTF-8" action="/people/search?class=nifty_form&method=get" method="post">'
#====== 正确用法
form_tag({ controller: "people", action: "search" }, method: "get", class: "nifty_form")
#=> '<form accept-charset="UTF-8" action="/people/search" class="nifty_form" method="get">'
上方的错误用法为form_tag
将 method: "get", class: "nifty_form"
当作网址的参数,因此我们必须要将:controller
,:action
用大括号包住。
这里很容易被雷到,所以一定要注意!而由上述的例子我们也可以知道,当我们要传get
参数,可以用上面用法进行参数传递
不免俗的讲解一下样式
= tab_content(blog_genre.second[:id]) do
- form_tag({ controller: 'admin/blogs', action: 'special_update' },
method: "post", class: "nifty_form")
- @ten_blogs.each do |blog|
= text_field(:blog, :title, multiple: true)
= text_field(:blog, :content, multiple: true)
由於 input element
为行内排列,因此做简单的排版後,输入框会排成一列。
Inline element 无法用区块的方式控制,因此<input>
无法用padding
, margin
控制。
另外加了multiple: true
後,可以允许传复数值,因此name属性
会从blog[title]
转为 blog[title][]
,但不过这也不是我们想要的样子。因此我们直接暴力的将name属性
写在上面,并且仿照simple_form
刻画画面的方式做样式的撰写,最後版本如下:
- form_tag({ controller: 'admin/blogs', action: 'special_update' },
method: "post", class: "nifty_form")
= grid_div(1, 1, style: "column-gap: 10px;") do
- @ten_blogs.each.with_index(1) do |blog, index|
= card do
= card_header(title: "第#{index}笔资料")
= card_body do
= tag.div class: 'form-group blog__title'
= label_tag :blog__title, "标题"
= text_field_tag "blog[][title]", nil, class: 'form-control string required'
= tag.div class: 'form-group string required blog__content'
= label_tag :blog__content, "内文"
= text_field_tag "blog[][content]", nil, class: 'form-control string required'
= grid_div(1, 10) do
= label_tag nil, "种类"
= tag.div do
- [["生活", :life], ["休闲", :casual], ["科技", :technology]].each do |e|
= tag.div class: 'form-check form-check-inline'
= radio_button_tag "blog[][genre]", e.second, false
= label_tag :blog__genre, e.first
再来我们开始填写预设值进去
- form_tag({ controller: 'admin/blogs', action: 'special_update' },
method: "post", class: "nifty_form")
= grid_div(1, 1, style: "column-gap: 10px;") do
- @ten_blogs.each.with_index(1) do |blog, index|
= card do
= card_header(title: "第 #{index} 笔资料")
= card_body do
= tag.div class: 'form-group blog__title'
= label_tag :blog__title, "标题"
= text_field_tag "blog[][title]", blog.title,
class: 'form-control string required'
= tag.div class: 'form-group string required blog__content'
= label_tag :blog__content, "内文"
= text_field_tag "blog[][content]", blog.content,
class: 'form-control string required'
= grid_div(1, 10) do
= label_tag nil, "种类"
= tag.div do
- [["生活", :life], ["休闲", :casual], ["科技", :technology]].each do |e|
= tag.div class: 'form-check form-check-inline'
= radio_button_tag "blog[][genre]",
e.second, e.second === blog.genre&.to_sym
= label_tag :blog__genre, e.first
下列的文字是我们设定的预设文字
上述情境并非依照惯例撰写,因此程序码会写得比较多行。不过由此我们可以细细品察form_tag
的用法
接着,我们回到Day23的例子,我们看一下在simple_form
中到底帮忙radio button
做多少事情,以至於我们不用像刚刚在form_tag
时写得那麽多
= f.input :genre, as: :radio_buttons, label: tag.strong('分类'),
collection: [["生活", :life], ["休闲", :casual], ["科技", :technology]],
selected: :casual, item_wrapper_class: 'form-check form-check-inline'
上面的slim
所对应的html
如下
<fieldset class="form-group radio_buttons required blog_genre">
<legend class="col-form-label pt-0">
<strong>分类</strong> <abbr title="required">*</abbr>
</legend>
<input type="hidden" name="blog[genre]" value="" />
<div class="form-check form-check-inline">
<input
class="form-check-input radio_buttons required"
type="radio"
value="life"
name="blog[genre]"
id="blog_genre_life"
/><label
class="form-check-label collection_radio_buttons"
for="blog_genre_life"
>生活</label>
</div>
<div class="form-check form-check-inline">
<input
class="form-check-input radio_buttons required"
selected="selected"
type="radio"
value="casual"
name="blog[genre]"
id="blog_genre_casual"
/><label
class="form-check-label collection_radio_buttons"
for="blog_genre_casual"
>休闲</label>
</div>
<div class="form-check form-check-inline">
<input
class="form-check-input radio_buttons required"
type="radio"
value="technology"
name="blog[genre]"
id="blog_genre_technology" /><label
class="form-check-label collection_radio_buttons"
for="blog_genre_technology"
>科技</label>
</div>
</fieldset>
下面一行为隐藏区块,这是rails
的贴心设计。当没有填值时,会传一个空字串。
<input type="hidden" name="blog[genre]" value="" />
接着我们来看看 controller 的逻辑
在资料库只创建到第二笔的情况下,当我们传了新的一笔,该笔的id
会等於3。当我们表单送出时,会在第3笔资料显示资料。
以下为新增表单
按下送出按钮时,从画面传过来的值
params
#=> <ActionController::Parameters {"authenticity_token"=>"an8nw1/cmx7KOMq42araGAwTHwA894VnMDszcCBKTbBlxHQGj1l8nmCs79wgh55aoUSOG4/Ta9VxupUmSENi2Q==", "blog"=><ActionController::Parameters {"title"=>"哈罗", "content"=>"哈罗", "genre"=>"casual"} permitted: false>, "button"=>"", "controller"=>"admin/blogs", "action"=>"create"} permitted: false>
blog_params
#=> <ActionController::Parameters {"title"=>"哈罗", "content"=>"哈罗", "genre"=>"casual"} permitted: true>
按下送出後,就会触发controller#create
逻辑。以下为送出表单的 controller#create
逻辑
module Admin
class BlogsController < ApplicationController
def index
# 单笔资料新增一笔待储存的纪录
@blog = current_user.blogs.new
end
# 创建一笔新的纪录
def create
if current_user.blogs.create(blog_params).persisted?
flash[:notice] = '建立成功'
else
flash[:alert] = '建立失败'
end
redirect_to admin_blogs_path
end
private
# 单笔资料用
def blog_params
params.require(:blog).permit(:title, :content, :genre)
end
end
end
下列为上传 id: 1-4
笔,送出後重新导向原画面的动作
下列为从画面传过去的 params
params
#=> <ActionController::Parameters {"authenticity_token"=>"yG9dFBySr22K4cTR8AwBivWqn03ZYr6ZL/8HIEISBpvH1A7RzBdI7SB14bUJIUXIWP0OVmpGUCtufqF2Khsp8g==", "blog"=>[<ActionController::Parameters {"title"=>"Title 1", "content"=>"Content 1", "genre"=>"life"} permitted: false>, <ActionController::Parameters {"title"=>"cxzbdsb", "content"=>"hrahr"} permitted: false>, <ActionController::Parameters {"title"=>"", "content"=>""} permitted: false>, <ActionController::Parameters {"title"=>"", "content"=>""} permitted: false>], "commit"=>"送出", "controller"=>"admin/blogs", "action"=>"special_update"} permitted: false>
special_blog_params
#=> [<ActionController::Parameters {"title"=>"标题1", "content"=>"内文1"} permitted: true>,
# <ActionController::Parameters {"title"=>"", "content"=>""} permitted: true>,
# <ActionController::Parameters {"title"=>"标题2", "content"=>"6666", "genre"=>"life"} permitted: true>,
# <ActionController::Parameters {"title"=>"", "content"=>""} permitted: true>]
按下送出後,就会触发controller#special_update
逻辑。以下为送出表单的 controller#special_update
逻辑
module Admin
class BlogsController < ApplicationController
def index
# 取得id: 1-4 的资料,若娶不到则会新增一笔待储存的纪录
@ten_blogs = SPECIAL_ARTICLE.map { |n| Blog.find_or_initialize_by(id: n) }
end
# 创建或修改id: 1-4
def special_update
blogs = Blog.where(id: SPECIAL_ARTICLE)
special_blog_params.each.with_index(1) do |params, index|
next unless params.values.any?(&:present?)
blog = blogs.find_or_initialize_by(id: index)
blog.title = params[:title]
blog.content = params[:content]
blog.genre = params[:genre]
blog.user = current_user
blog.save
end
flash[:notice] = '建立成功'
redirect_to admin_blogs_path
end
private
# 多笔资料用
def special_blog_params
params.permit(blog: [:title, :content, :genre]).try(:[], :blog)
end
end
end
今天主要讲的是表单的部分,以下整理出这章节讲到的几个重点
form_tag
的使用
simple_form
对於radio_button
的使用
<input>
为行内元素,并非区块元素
前几天我们也讲到<button>
也为行内元素,要记住喔!
controller
针对单笔新增
/ 多笔新增
的处理
目前尚未介绍的内容
>>: Day10:10 - 商品服务(1) - 後端 - 总商品资料API
前几天有练习了小蜘蛛和跳过鱼 今天还是持续练习Web的工具 透过这些工具可以辅助我们更顺利进行手动测...
当我在Unreal Engine 4.27.0下载好试图启动软件时,跳出了下面这个视窗, U...
前面介绍的程序都只有单一路径,今天要来讲更复杂的多路径的情况 运算子 比较运算子 说明 用法 ==...
在开始之前,还是很惊讶自己有天可以在这里写文章,分享自身所学的IT技术,提供给大家参考。那其实我...
1.NPM版本 无须更新到最新,怕错误 2.制作专案package.json npm init np...