分享 如何理解 form_for 和 form_tag

jesktop · 2014年09月14日 · 最后由 superbear 回复于 2014年09月20日 · 17716 次阅读
本帖已被管理员设置为精华贴

Form_for 是什么

在我们使用 Rails 的过程中,必然离不开Form_for,甚至在刚入门没有多长的时间里就会接触到Form_for。本来Form_for并不是一个如何特别神奇的东西,无非就是一个 Helper,Helper 的作用是什么?对于 Helper 而言,无论它的逻辑多么的复杂,其实本质的作用就是生成 HTML 代码。所以我们应该以 HTML 的角度,去理解Form_for.
为什么特别写一篇文章来讲Form_for呢?主要的原因是之前有个人问了我一个这样的问题:
<input type="reset"/>是用于重置表单的,可是我并不知道应该怎么放入Form_for当中!
关于这类型的问题,我已经遇到很多次了,例如说 Boostrap 中关于一些 form 的样式代码,不知道如何应用到Form_for中之类。那究竟关于<form>里的一些事情,如何应用到Form_for中呢? 先看看官方的文档怎么介绍Form_for的:

<%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
  <%= f.text_field :title %>
  <%= f.text_area :body, size: "60x12" %>
  <%= f.submit "Create" %>
<% end %>

生成如下的 HTML:

<form accept-charset="UTF-8" action="/articles/create" method="post" class="nifty_form">
  <input id="article_title" name="article[title]" type="text" />
  <textarea id="article_body" name="article[body]" cols="60" rows="12"></textarea>
  <input name="commit" type="submit" value="Create" />
</form>

其实如果你愿意的话,上面的Form_for是可以混着 HTML 写的,当然我觉得这不会是一个好的案例,如:

<%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
  <%= f.text_field :title %>
  <textarea id="article_body" name="article[body]" cols="60" rows="12"></textarea>
  <%= f.submit "Create" %>
<% end %>

所以对于之前如何添加<input type="reset"/>的问题来说,答案已经很清晰了。当然如何解决这个问题并不是主要想讨论的事情,关键的一点是非常多的初学者对Form_for的理解误区,他们普遍觉得在Form_for里写 input 是应该像这样的:<%= f.text_field :title %>,而不是<input id="article_title" name="article[title]" type="text"/>,关键是忽略了Form_for主要责任是生成 HTML 而已。所以特别需要记住的一点是,就算是在 Rails 里,HTTP 的表单请求依然是依赖 HTML 的<form></form>标签,而不是 Rails 的Form_forHelper,而Form_for是用于生成<form></form>。所以这个问题的产生其实是源自于对 HTML 本身的不够熟悉。

Form_for 与 Form_tag

好了,那说完以上的问题后,在说说那Form_forForm_tag的区别是什么?其实它们的区别就是生成的 HTML 不一样,而有非常一大部分的初学者会认为他们的区别是一个用于创建和更新用,对于搜索就使用Form_tag。初学者的观点也不能说不对,但是如果要了解为什么需要在不同的情况使用不同的 Helper 还是需要了解一下它们的区别。那它们生成的 HTML 究竟有什么不一样呢?
关于Form_for的例子,可以看上面提到的那个例子,而现在看看官方文档中对Form_tag的描述是怎么样的:

<%= form_tag("/search", method: "get") do %>
  <%= label_tag(:q, "Search for:") %>
  <%= text_field_tag(:q) %>
  <%= submit_tag("Search") %>
<% end %>

生成如下的 HTML:

<form accept-charset="UTF-8" action="/search" method="get"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="&#x2713;" /></div>
  <label for="q">Search for:</label>
  <input id="q" name="q" type="text" />
  <input name="commit" type="submit" value="Search" />
</form>

它们最大的区别和最关键的区别就是 input 中的 name 值,分别是:Form_forname="article[body]",而Form_tagname="q",而这个 name 值决定了数据发往服务器时的格式是怎么的:Form_for传出去的格式是{article: {body: value}},而Form_tag则是{q: value}。关于article这个名称的定义则来源于form_for @article中的@article
然后清楚了它们的两个的区别后,又会衍生另一个问题,就是,那我用Form_for做搜索不就也可以了?答案是:当然可以。但这里会有一个问题,如果你直接这样使用form_for @search的话,整个页面就会出错了。原因在于 Rails 并不知道@search是不是一个 hash 类型,而且并没有定义里面的参数值,所以你这样定义就可以顺利通过了:@search = {q: ""}。同理可得,其实@article = Article.new就相当是一个给予article默认 key 值和默认 value 值的过程。所以其实无论是Form_forForm_tag都好,Rails 无形中都帮我们把整个概念整理的更简洁了。
所以在创建和更新方面常见的操作为@article = Article.new(params[:article]),而在 search 中则多为@article = Article.search(name: params[:name])

原文: 如何理解 FORM_FOR 和 FORM_TAG

文章不错!博客主题和 Rei 的一样 😓 话说最近加精文章倍增啊~感觉是大家纷纷响应社区呼唤一样~ 😄

天呐... 区别已经很明显了,还有人理解得这么'深入',有的地方还理解错了。

#1 楼 @itzzq 是哦。找了好多免费的模板,还是 Rei 用的那套好看啊。😄

form_for 中有 object 的概念,block 中需要有一个参数 t,t.object 为 form_for 第一个参数对象

form_tag 就是普通的 html form

/Users/david/.rvm/gems/ruby-2.0.0-p481@xxx/gems/actionpack-3.2.19/lib/action_view/helpers/form_helper.rb

def form_for(record, options = {}, &block)
        raise ArgumentError, "Missing block" unless block_given?

        options[:html] ||= {}

        case record
        when String, Symbol
          object_name = record
          object      = nil
        else
          object      = record.is_a?(Array) ? record.last : record
          object_name = options[:as] || ActiveModel::Naming.param_key(object)
          apply_form_for_options!(record, options)
        end

        options[:html][:remote] = options.delete(:remote) if options.has_key?(:remote)
        options[:html][:method] = options.delete(:method) if options.has_key?(:method)
        options[:html][:authenticity_token] = options.delete(:authenticity_token)

        builder = options[:parent_builder] = instantiate_builder(object_name, object, options, &block)
        output  = capture(builder, &block)
        default_options = builder.multipart? ? { :multipart => true } : {}
        form_tag(options.delete(:url) || {}, default_options.merge!(options.delete(:html))) { output }
      end

form_for 无非就是处理了下参数,然后最后调用 form_tag

Boostrap 有入门的资料吗?

#6 楼 @fztree 官网的 document 还不够入门么~,不复杂~

好文赞个

这么理解对不对,form_for 向服务器传递的是各个属性封装好的 model,再把 model 放在了 params 这个 hash 里,而 form_tag 传给服务器的就是 model 的一个个属性,都放在了 params 里面。

#9 楼 @shangrenzhidao 我觉得 #5 楼 给的源码讲的挺好的。 form_for 是生成 html,不会跟服务器产生什么联系的,跟服务器联系的是 html 的

,可以看看怎么和服务器产生联系的。 所以 form_for 是根据封装好的 model 生成对应的 html,特别是 input 里面的 name 和 value 属性,如你 model 是 product 且有字段为 name,那就生成的 name 为 name="product[name]",而如果是 edit 的情况,还会赋予对应的 value 值。 而 form_tag 则不对 objec 进行操作,且生成的 input 的 name="xxx"。 不知道我讲的清楚吗?

主要的原因是之前有个人问了我一个这样的问题: 是用于重置表单的,可是我并不知道应该怎么放入 Form_for 当中!

这个问题就是我问的,小伙伴们可以叫我:丧 B。目前初学 RoR,师从于 Jesktop。 特来顶贴。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号