Rails Form helpers

最基本的form helper是form_tag: 
Ruby代码   收藏代码
  1. <% form_tag do %>   
  2.  Form contents   
  3. <% end %>   

没给form_tag传递任何参数的时候,form_tag将生成一个以当前页面为action值,以post为method值的form元素,如下: 
Html代码   收藏代码
  1. <form action="/books" method="post">  
  2.  <div style="margin:0;padding:0;display:inline">  
  3.   <input name="authenticity_token" type="hidden"   
  4.   value="1ZpTUU7D9I2lhajgvAiXlu+JwxujHl4CSfczZWh/aM4=" />  
  5.  </div>  
  6.   Form contents  
  7. </form>   

这里自动生成了一个隐藏的div,里面有一个隐藏域,name为authenticity_token,value是一个随机字符串,这个隐藏域的值是用于跨站点请求伪造保护(cross-site request forgery protection)的。 关于跨站请求伪造。 You can read more about this in the  Ruby On Rails Security Guide

form_tag的第一个参数是表单的action值;第二个参数是个hash,可以用来设置表单的method属性和multipart、class等html属性。 

rails还提供text_field_tag、label_tag、password_field_tag、hidden_field_tag等常用表单元素的helper,利用这些helper可以组成一个完整的表单。 

通常情况下,页面的一个表单是和某个model绑定的,假如用上面这些text_field_tag等带_tag后缀的标签来绑定model,需要为这些helper指定name,示例: 
Ruby代码   收藏代码
  1. <% form_tag book_path(@book), :method => :put do%>  
  2.     <%= text_field_tag 'book[title]'@book.title %><br/>  
  3.     <%= text_field_tag 'book[author]'@book.author %><br/>  
  4.     <%= submit_tag 'Update' %>  
  5. <% end %>  

假设这里的@book有属性title='refactor',author='martin fowler'生成的 
Html代码   收藏代码
  1. <form action="/books/1" method="post">  
  2.  <div style="margin:0;padding:0;display:inline">  
  3.   <input name="_method" type="hidden" value="put" />  
  4.   <input name="authenticity_token" type="hidden"  
  5.   value="1ZpTUU7D9I2lhajgvAiXlu+JwxujHl4CSfczZWh/aM4=" />  
  6.  </div>  
  7.  <input id="book_title" name="book[title]" type="text" value="refactor"/><br/>  
  8.  <input id="book_author" name="book[author]" type="text" value="martin fowler"/><br/>  
  9.  <input name="commit" type="submit" value="Update" />  
  10. </form>  

这里边的重复代码很多,比如book[title]和@book.title。rails提供了更方便的另一系列helper,这些helper不带_tag后缀,如:text_field、text_area。这些helper的第一个参数是model实例变量的名字,第二个参数是model的方法(一般情况下就是model的属性) 

Ruby代码   收藏代码
  1. <% form_tag book_path(@book), :method => :put do%>  
  2.     <%= text_field :book:title %><br/>  
  3.     <%= text_field :book:author %><br/>  
  4.     <%= submit_tag 'Update' %>  
  5. <% end %>  

这段erb代码和之前的*_tag生成的html差不多。并不是完全一样: 
Html代码   收藏代码
  1. <form action="/books/1" method="post">  
  2.  <div style="margin:0;padding:0;display:inline">  
  3.   <input name="_method" type="hidden" value="put" />  
  4.   <input name="authenticity_token" type="hidden"  
  5.   value="1ZpTUU7D9I2lhajgvAiXlu+JwxujHl4CSfczZWh/aM4=" />  
  6.  </div>  
  7.  <input id="book_title" name="book[title]" size="30" type="text" value="refactor" /><br/>  
  8.  <input id="book_author" name="book[author]" size="30" type="text" value="martin fowler" /><br/>  
  9.  <input name="commit" type="submit" value="Update" />  
  10. </form>  

这段erb代码比较之前的干净多了,但是这里还有重复代码:model的名字。rails提供了form_for来消除这个重复:
Ruby代码   收藏代码
  1. <% form_for :book@book:url=>book_path(@book), :html=>{:method=>:putdo |f| %>  
  2.     <%= f.text_field :title%><br/>  
  3.     <%= f.text_field :author%><br/>  
  4.   <%= f.submit 'Update' %>  
  5. <% end %>  

这段代码也能达到之前的效果,注意这里的text_field等方法是block内参数f的方法。form_for的第一个参数是model的名字,第二个参数是一个model的实例(一般来自controller),如果这个实例的变量名和model名一样,可以把第2个参数省略掉,写成这样:
Ruby代码   收藏代码
  1. <% form_for :book:url=>book_path(@book), :html=>{:method=>:putdo |f| %>  

假如表单中有相关联的其它model的属性,还可以使用fields_for: 
Ruby代码   收藏代码
  1. <% form_for :person:url=>person_path(@person), :html=>{:method=>:putdo |f| %>  
  2.   name:<%= f.text_field :name %><br/>  
  3.   <%fields_for @person.address do |address|%>  
  4.   city:<%=address.text_field :city%><br/>  
  5.   street:<%=address.text_field :street%><br/>  
  6.   <%end%>  
  7.   <%= f.submit 'Update' %>  
  8. <% end %>  

fields_for的用法跟form_for差不多,以上代码生成如下HTML: 
Html代码   收藏代码
  1. <form action="/people/2" method="post">  
  2.  <div style="margin:0;padding:0;display:inline">  
  3.   <input name="_method" type="hidden" value="put" />  
  4.   <input name="authenticity_token" type="hidden"  
  5.   value="hJMxllYlzx0oWi2swhw1Hq3P3yfYMMwL+KXdjOBwXmM=" />  
  6.  </div>   
  7.  name:<input id="person_name" name="person[name]" size="30" type="text" value="bob" /><br/>     
  8.  city:<input id="address_city" name="address[city]" size="30" type="text" /><br/>   
  9.  street:<input id="address_street" name="address[street]" size="30" type="text" /><br/>     
  10.  <input id="person_submit" name="commit" type="submit" value="Update" />   
  11. </form>   

其中city和street的值可以在controller通过params[:address]取得。 

Rails2.3开始支持嵌套的对象表单(Nested Object Form)。 在update操作的时候,上面的表单在controller里必须通过@person.address.build(params[:address])来更新address。使用嵌套对象表单可以直接通过@person.update_attributes(params[:person])一句代码来同时完成@person以及@person.address的更新。要使用嵌套对象表单首先要给model添加一句代码,比如这里的Person类: 
Ruby代码   收藏代码
  1. class Person < ActiveRecord::Base  
  2.     has_one :address  
  3.     accepts_nested_attributes_for :address  
  4. end  

这里加了一句accepts_nested_attributes_for方法的调用,erb代码可以这样写: 
Ruby代码   收藏代码
  1. <% form_for @person:url=>person_path(@person), :html=>{:method=>:putdo |f| %>  
  2.   name:<%= f.text_field :name %><br/>  
  3.   <%f.fields_for :address do |address|%>  
  4.   city:<%=address.text_field :city%><br/>  
  5.   street:<%=address.text_field :street%><br/>  
  6.   <%end%>  
  7.   <%= f.submit 'Update' %>  
  8. <% end %>  

再次注意这里的fields_for,是form_for方法的block里参数f的方法。 
生成的HTML: 
Html代码   收藏代码
  1. <form action="/people/2" class="edit_person" id="edit_person_2" method="post">  
  2.  <div style="margin:0;padding:0;display:inline">  
  3.   <input name="_method" type="hidden" value="put" />  
  4.   <input name="authenticity_token" type="hidden"   
  5.   value="hJMxllYlzx0oWi2swhw1Hq3P3yfYMMwL+KXdjOBwXmM=" />  
  6.  </div>   
  7.  name:<input id="person_name" name="person[name]" size="30" type="text" value="bob" /><br/>   
  8.  <input id="person_address_attributes_id" name="person[address_attributes][id]" type="hidden" value="1" />   
  9.  city:<input id="person_address_attributes_city" name="person[address_attributes][city]" size="30" type="text" value="sanming" /><br/>   
  10.  street:<input id="person_address_attributes_street" name="person[address_attributes][street]" size="30" type="text" value="xxstreet" /><br/>   
  11.     
  12.  <input id="person_submit" name="commit" type="submit" value="Update" />   
  13. </form>   

对比之前的那段HTML,不难发现,这段HTML多了一个隐藏域,用来存放address的id: 
Html代码   收藏代码
  1. <input id="person_address_attributes_id" name="person[address_attributes][id]" type="hidden" value="1" />  

同时,city、street输入框的id和name属性和之前也不一样了。比较郁闷的是,Nested Object Form似乎只对update操作有效,create操作就不行,因为在new的页面上得不到address的id,会出现“You have a nil object when you didn't expect it!“。因此,new页面只好使用前面关于fields_for方法的第一个例子中的方式来创建表单了。(仍然有解决的办法: http://www.pixellatedvisions.com/2009/03/18/rails-2-3-nested-model-forms-and-nil-new-record  只要在fields_for之前build_address即可。) 

还要注意的是,使用Nested Object Form的时候,form_for的第一个参数不能再是symbol,而必须是一个object,假如使用symbol :person,生成的HTML会变成下面这样: 
Html代码   收藏代码
  1. <form action="/people/2" method="post">  
  2.  <div style="margin:0;padding:0;display:inline">  
  3.   <input name="_method" type="hidden" value="put" />  
  4.   <input name="authenticity_token" type="hidden"  
  5.   value="hJMxllYlzx0oWi2swhw1Hq3P3yfYMMwL+KXdjOBwXmM=" />  
  6.  </div>   
  7.  name:<input id="person_name" name="person[name]" size="30" type="text" value="bob" /><br/>     
  8.  city:<input id="person_address_city"  
  9.  name="person[address][city]" size="30" type="text" /><br/>   
  10.  street:<input id="person_address_street"  
  11.  name="person[address][street]" size="30" type="text" /><br/>     
  12.  <input id="person_submit" name="commit" type="submit" value="Update" />   
  13. </form>   

少了那个隐藏域,而且city和street输入框的id和name属性又不一样了,这样的表单提交之后会出现TypeMismatch Exception。见: http://stackoverflow.com/questions/742536/rails-nested-object-form-attributes-problem  

accepts_nested_attributes_for方法的调用会为该类的实例生成一个名为*_attributes=的writer method。详见 Rails API中fields_for方法的Nested Attributes Example部分 。 

最后,RESTful的应用中,对作为资源的model,form_for方法用起来更简单: 
Ruby代码   收藏代码
  1. <%form_for(@persondo |f|%>  
  2. .....  
  3. <%end%>  

这里生成的form元素根据@person的状态来设置action属性:如果@person.new_record?返回true,action中使用的相应的path就是people_path,并且method属性将被设置为post;否则action中使用的相应的path是person_path(@person),并且method属性将被设置为put。再仔细观察,两种情况下form元素的id和class值也会不同:
引用
a form creating an article would have id and class new_article. If you were editing the article with id 23, the class would be set to edit_article and the id to edit_article_23.


使用了命名空间(namespace)的resource由于path/url helper与未使用命名空间的resource的path/url helper不同,直接使用form_for(@blog)会找不到blogs_path方法。这种情况下可以这样使用form_for: 
Ruby代码   收藏代码
  1. <% form_for [:admin@blog]) do |f|%>  
  2. ...  
  3. <% end %>  

这里会自动使用admin_blogs_path或者admin_blog_path(@blog)(视@blog.new_record?的返回值而定),相当于: 
Ruby代码   收藏代码
  1. <% form_for(:blog@blog:url=>admin_blogs_path) do |f|%>  
  2. ...  
  3. <% end %>  

或 
Ruby代码   收藏代码
  1. <% form_for(:blog@blog:url=>admin_blog_path(@blog), :html=>{:method=>:put}) do |f|%>  
  2. ...  
  3. <% end %>  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值