Action View:Helpers
Measuring programming progress by lines of code is like measuring aircraft building progress by weight. - Bill Gates
在Rails中, Helper 指的是可以在Template中使用的辅助方法,主要 用途是可 以将资料转化成输出用的 HTML字串,例如我们已经用过了Rails内建的link_to方法,它可以将字串变成超连结。 Rails还内建了许多Helper方法,可以让我们建构HTML更为容易。 我们在一章中将介绍其中较为常用的几个方法。
另一个使用Helper的理由是可以简化Template中的复杂结构,将Template中较为复杂的程式都用Helper包装起来,最好让Template只包含单纯的变数以及最简单的条件逻辑和回圈,这样就算是不会程式的网页设计师,也能够轻易了解套版甚至修改Template样板。
因为Helper预设只能在Template中使用,如果想在rails console中呼叫,必须加上helper ,例如helper.link_to 。 另外,虽然机会不多,如果真的要在Rails Controller或Model程式中呼叫Helper ,则可以加上ApplicationController.helpers前置词。
静态档案辅助方法
使用Rails内建的静态档案(Assets)辅助方法有几个好处:
Rails会合并Stylesheet和JavasSript档案,可以加速浏览器的下载。
Rails会编译Sass和CoffeeScript等透过Assets template engine产生的Stylesheet和JavasSript
Rails会在静态档案网址中加上时间序号,如果内容有修改则会重新产生。 这样的好处是强迫用户的浏览器一定会下载到最新的版本,而不会有浏览器快取到旧版本的问题。
变更Assets host主机位址时,可以一次搞定,例如上CDN时。 透过Helpers , Rails可以帮所有的Assets加上静态档案伺服器网址。
几个常用的方法:
javascript_include_tag
stylesheet_link_tag
image_tag
video_tag
audio_tag
格式化辅助方法
simple_format
将\n换行字元换成HTML的<br>标签
truncate
撷取前n个字元
sanitize
白名单逸出
预设允许的HTML标签和属性如下:
ActionView::Base.sanitized_allowed_tags => #<Set: {"strong", "em", "b", "i", "p", "code", "pre", "tt", "samp", "kbd", "var", "sub", "sup", "dfn", "cite", "big", "small", "address", "hr", "br", "div", "span", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "li", "dl", "dt", "dd", "abbr", "acronym", "a", "img", "blockquote", "del", "ins"}> ActionView::Base.sanitized_allowed_attributes => #<Set: {"href", "src", "width", "height", "alt", "cite", "datetime", "title", "class", "name", "xml:lang", "abbr"}>
如果需要增加,可以在config/application.rb中新增,例如:
config.action_view.sanitized_allowed_tags = %w[table tr td] config.action_view.sanitized_allowed_attributes = "rel"
strip_tags
移除HTML标签
strip_links
移除HTML超连结标签
URL辅助方法
link_to 文字超连结
mail_to E-mail
button_to 按钮连结
自定标签辅助方法
tag
content_tag
其他辅助方法
escape_javascript
debug
number_to_currency
表单辅助方法
对网页应用程式来说,表单是非常重要的用户输入介面。 Rails在这方面也提供了很多好用的Helper方法。 基本上, Rails处理表单分成两种类型:
一种是对应到Model物件的新增、修改,我们会使用form_for这个Helper 。 它的好处在于透过传入Model物件,可以在修改的时候自动帮你将预设值带入。 例如我们已经在Part1使用过的event表单:
<%= form_for @event do |f| %> <%= f.text_field :name %> <%= f.submit %> <% end %>
另一种是就是没有对应Model的表单,我们使用form_tag这个方法。 例如:
<%= form_tag "/search" do %> <%= text_field_tag :keyword %> <%= submit_tag %> <% end %>
和form_tag有些类似,但是其中不需要传Block变数f ,其中的栏位Helper需要多加_tag结尾。 不像form_for的栏位名称一定要是Model的属性之一,在form_tag之中的栏位名称则完全不受限。
几个常用的表单栏位辅助方法:
text_field
text_area
radio
check_box
select
select_date, select_datetime
submit
如何处理Model中不存在的属性
使用form_for时,其中的栏位必须是Model有的属性,那如果资料库没有这个栏位呢?这时候你依需要在Model程式中加上存取方法,例如:
class Event < ActiveRecord::Base #... def custom_field # 根據其他屬性的值或條件,來決定這個欄位的值end def custom_field=(value) # 根據value,來調整其他屬性的值end end
这样就可以在form_for里使用custom_field了。
<%= form_for @event do |f| %> <%= f.text_field :custom_field %> <%= f.submit %> <% end %>
资料验证错误时的处理
当Model物件储存失败时,我们通常会重新显示表单,这时候该怎么显示Model的错误讯息呢?以下是一个预设的范例:
<%= form_for(@person) do |f| %> <% if @person.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@person.errors.count, "error") %> prohibited this person from being saved:</h2> <ul> <% @person.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <%= f.text_field :name %> <%= f.submit %> <% end %>
透过检查@person.errors我们可以把所有的错误讯息显示出来。 除了这种作法,我们也可以把错误讯息放在输入框的旁边:
<%= form_for(@person) do |f| %> <%= f.text_field :name %> <% if @person.errors[:name].presence %> <%= @person.errors[:name].join(", ") %> <% end %> <%= f.submit %> <% end %>
在Rails 2版本中有error_messages_for和error_message_on方法, Rails 3则被移成Plugin http://github.com/rails/dynamic_form
自定Helper
要建立自定的Helper ,只需要将方法定义在app/helpers/目录下的任意档案就可以了。 在产生Controller的同时, Rails就会自动产生一个同名的Helper档案,照惯例该Controller下的Template所用的Helper ,就放在该档案下。 如果是全站使用的Helper ,则会放在app/helpers/application_helper_rb ,例如:
module ApplicationHelper def gravatar_url(email) gravatar_email = Digest::MD5.hexdigest(email.downcase) return "http://www.gravatar.com/avatar/#{gravatar_email}?s=48" end end
如此便可以在Template中这样使用:
<%= image_tag gravatar_url(user.email) %>
Helper是全域的,定义在哪一个档案中没有关系,档案名称也不需要与Controller名称对应。