# Action View 基礎
讀完本文,你將學到:
* Action View 是什么,如何在 Rails 中使用;
* 模板、局部視圖和布局的最佳使用方法;
* Action View 提供了哪些幫助方法,如何自己編寫幫助方法;
* 如何使用本地化視圖;
* 如何在 Rails 之外的程序中使用 Action View;
### Chapters
1. [Action View 是什么?](#action-view-%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F)
2. [在 Rails 中使用 Action View](#%E5%9C%A8-rails-%E4%B8%AD%E4%BD%BF%E7%94%A8-action-view)
3. [模板,局部視圖和布局](#%E6%A8%A1%E6%9D%BF%EF%BC%8C%E5%B1%80%E9%83%A8%E8%A7%86%E5%9B%BE%E5%92%8C%E5%B8%83%E5%B1%80)
* [模板](#%E6%A8%A1%E6%9D%BF)
* [局部視圖](#%E5%B1%80%E9%83%A8%E8%A7%86%E5%9B%BE)
* [布局](#%E5%B8%83%E5%B1%80)
4. [局部布局](#%E5%B1%80%E9%83%A8%E5%B8%83%E5%B1%80)
5. [視圖路徑](#%E8%A7%86%E5%9B%BE%E8%B7%AF%E5%BE%84)
6. [Action View 提供的幫助方法簡介](#action-view-%E6%8F%90%E4%BE%9B%E7%9A%84%E5%B8%AE%E5%8A%A9%E6%96%B9%E6%B3%95%E7%AE%80%E4%BB%8B)
* [`RecordTagHelper`](#recordtaghelper)
* [`AssetTagHelper`](#assettaghelper)
* [`AtomFeedHelper`](#atomfeedhelper)
* [`BenchmarkHelper`](#benchmarkhelper)
* [`CacheHelper`](#cachehelper)
* [`CaptureHelper`](#capturehelper)
* [`DateHelper`](#datehelper)
* [`DebugHelper`](#debughelper)
* [`FormHelper`](#formhelper)
* [`FormOptionsHelper`](#formoptionshelper)
* [`FormTagHelper`](#formtaghelper)
* [`JavaScriptHelper`](#javascripthelper)
* [`NumberHelper`](#numberhelper)
* [`SanitizeHelper`](#sanitizehelper)
7. [視圖本地化](#%E8%A7%86%E5%9B%BE%E6%9C%AC%E5%9C%B0%E5%8C%96)
### 1 Action View 是什么?
Action View 和 Action Controller 是 Action Pack 的兩個主要組件。在 Rails 中,請求由 Action Pack 分兩步處理,一步交給控制器(邏輯處理),一步交給視圖(渲染視圖)。一般來說,Action Controller 的作用是和數據庫通信,根據需要執行 CRUD 操作;Action View 用來構建響應。
Action View 模板由嵌入 HTML 的 Ruby 代碼編寫。為了保證模板代碼簡潔明了,Action View 提供了很多幫助方法,用來構建表單、日期和字符串等。如果需要,自己編寫幫助方法也很簡單。
Action View 的有些功能和 Active Record 綁定在一起,但并不意味著 Action View 依賴于 Active Record。Action View 是個獨立的代碼庫,可以在任何 Ruby 代碼庫中使用。
### 2 在 Rails 中使用 Action View
每個控制器在 `app/views` 中都對應一個文件夾,用來保存該控制器的模板文件。模板文件的作用是顯示控制器動作的視圖。
我們來看一下使用腳手架創建資源時,Rails 做了哪些事情:
```
$ rails generate scaffold post
[...]
invoke scaffold_controller
create app/controllers/posts_controller.rb
invoke erb
create app/views/posts
create app/views/posts/index.html.erb
create app/views/posts/edit.html.erb
create app/views/posts/show.html.erb
create app/views/posts/new.html.erb
create app/views/posts/_form.html.erb
[...]
```
Rails 中的視圖也有命名約定。一般情況下,視圖名和對應的控制器動作同名,如上所示。例如,`posts_controller.rb` 控制器中的 `index` 動作使用 `app/views/posts` 文件夾中的 `index.html.erb` 視圖文件。
返回給客戶端的完整 HTML 由這個 ERB 文件、布局文件和視圖中用到的所有局部視圖組成。后文會詳細介紹這幾種視圖文件。
### 3 模板,局部視圖和布局
前面說過,最終輸出的 HTML 由三部分組成:模板,局部視圖和布局。下面詳細介紹各部分。
#### 3.1 模板
Action View 模板可使用多種語言編寫。如果模板文件的擴展名是 `.erb`,使用的是 ERB 和 HTML。如果模板文件的擴展名是 `.builder`,使用的是 `Builder::XmlMarkup`。
Rails 支持多種模板系統,通過文件擴展名加以區分。例如,使用 ERB 模板系統的 HTML 文件,其擴展名為 `.html.erb`。
##### 3.1.1 ERB
在 ERB 模板中,可以使用 `<% %>` 和 `<%= %>` 標簽引入 Ruby 代碼。`<% %>` 標簽用來執行 Ruby 代碼,沒有返回值,例如條件判斷、循環或代碼塊。`<%= %>` 用來輸出結果。
例如下面的代碼,循環遍歷名字:
```
<h1>Names of all the people</h1>
<% @people.each do |person| %>
Name: <%= person.name %><br>
<% end %>
```
在上述代碼中,循環使用普通嵌入標簽(`<% %>`),輸出名字時使用輸出式嵌入標簽(`<%= %>`)。注意,這并不僅僅是一種使用建議:常規的輸出方法,例如 `print` 或 `puts`,無法在 ERB 模板中使用。所以,下面這段代碼是錯誤的:
```
<%# WRONG %>
Hi, Mr. <% puts "Frodo" %>
```
如果想去掉前后的空白,可以把 `<%` 和 `%>` 換成 `<%-` 和 `-%>`。
##### 3.1.2 Builder
Builder 模板比 ERB 模板需要更多的編程,特別適合生成 XML 文檔。在擴展名為 `.builder` 的模板中,可以直接使用名為 `xml` 的 `XmlMarkup` 對象。
下面是個簡單的例子:
```
xml.em("emphasized")
xml.em { xml.b("emph & bold") }
xml.a("A Link", "href" => "http://rubyonrails.org")
xml.target("name" => "compile", "option" => "fast")
```
輸出結果如下:
```
<em>emphasized</em>
<em><b>emph & bold</b></em>
<a href="http://rubyonrails.org">A link</a>
<target option="fast" name="compile" />
```
代碼塊被視為一個 XML 標簽,代碼塊中的標記會嵌入這個標簽之中。例如:
```
xml.div {
xml.h1(@person.name)
xml.p(@person.bio)
}
```
輸出結果如下:
```
<div>
<h1>David Heinemeier Hansson</h1>
<p>A product of Danish Design during the Winter of '79...</p>
</div>
```
下面這個例子是 Basecamp 用來生成 RSS 的完整代碼:
```
xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
xml.channel do
xml.title(@feed_title)
xml.link(@url)
xml.description "Basecamp: Recent items"
xml.language "en-us"
xml.ttl "40"
for item in @recent_items
xml.item do
xml.title(item_title(item))
xml.description(item_description(item)) if item_description(item)
xml.pubDate(item_pubDate(item))
xml.guid(@person.firm.account.url + @recent_items.url(item))
xml.link(@person.firm.account.url + @recent_items.url(item))
xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
end
end
end
end
```
##### 3.1.3 模板緩存
默認情況下,Rails 會把各個模板都編譯成一個方法,這樣才能渲染視圖。在開發環境中,修改模板文件后,Rails 會檢查文件的修改時間,然后重新編譯。
#### 3.2 局部視圖
局部視圖把整個渲染過程分成多個容易管理的代碼片段。局部視圖把模板中的代碼片段提取出來,寫入單獨的文件中,可在所有模板中重復使用。
##### 3.2.1 局部視圖的名字
要想在視圖中使用局部視圖,可以調用 `render` 方法:
```
<%= render "menu" %>
```
模板渲染到上述代碼時,會渲染名為 `_menu.html.erb` 的文件。注意,文件名前面有個下劃線。局部視圖文件前面加上下劃線是為了和普通視圖區分,不過加載局部視圖時不用加上下劃線。從其他文件夾中加載局部視圖也是一樣:
```
<%= render "shared/menu" %>
```
上述代碼會加載 `app/views/shared/_menu.html.erb` 這個局部視圖。
##### 3.2.2 使用局部視圖簡化視圖
局部視圖的一種用法是作為子程序,把細節從視圖中移出,這樣能更好的理解整個視圖的作用。例如,有如下的視圖:
```
<%= render "shared/ad_banner" %>
<h1>Products</h1>
<p>Here are a few of our fine products:</p>
<% @products.each do |product| %>
<%= render partial: "product", locals: {product: product} %>
<% end %>
<%= render "shared/footer" %>
```
在上述代碼中,`_ad_banner.html.erb` 和 `_footer.html.erb` 局部視圖中的代碼可能要用到程序的多個頁面中。專注實現某個頁面時,無需關心這些局部視圖中的細節。
##### 3.2.3 `as` 和 `object` 選項
默認情況下,`ActionView::Partials::PartialRenderer` 對象存在一個本地變量中,變量名和模板名相同。所以,如果有以下代碼:
```
<%= render partial: "product" %>
```
在 `_product.html.erb` 中,就可使用本地變量 `product` 表示 `@product`,和下面的寫法是等效的:
```
<%= render partial: "product", locals: {product: @product} %>
```
`as` 選項可以為這個本地變量指定一個不同的名字。例如,如果想用 `item` 代替 `product`,可以這么做:
```
<%= render partial: "product", as: "item" %>
```
`object` 選項可以直接指定要在局部視圖中使用的對象。如果模板中的對象在其他地方(例如,在其他實例變量或本地變量中),可以使用這個選項指定。
例如,用
```
<%= render partial: "product", object: @item %>
```
代替
```
<%= render partial: "product", locals: {product: @item} %>
```
`object` 和 `as` 選項還可同時使用:
```
<%= render partial: "product", object: @item, as: "item" %>
```
##### 3.2.4 渲染集合
在模板中經常需要遍歷集合,使用子模板渲染各元素。這種需求可使用一個方法實現,把數組傳入該方法,然后使用局部視圖渲染各元素。
例如下面這個例子,渲染所有產品:
```
<% @products.each do |product| %>
<%= render partial: "product", locals: { product: product } %>
<% end %>
```
可以寫成:
```
<%= render partial: "product", collection: @products %>
```
像上面這樣使用局部視圖時,每個局部視圖實例都可以通過一個和局部視圖同名的變量訪問集合中的元素。在上面的例子中,渲染的局部視圖是 `_product`,在局部視圖中,可以通過變量 `product` 訪問要渲染的單個產品。
渲染集合還有個簡寫形式。假設 `@products` 是一個 `Product` 實例集合,可以使用下面的簡寫形式達到同樣目的:
```
<%= render @products %>
```
Rails 會根據集合中的模型名(在這個例子中,是 `Product` 模型)決定使用哪個局部視圖。其實,集合中還可包含多種模型的實例,Rails 會根據各元素所屬的模型渲染對應的局部視圖。
##### 3.2.5 間隔模板
渲染局部視圖時還可使用 `:spacer_template` 選項指定第二個局部視圖,在使用主局部視圖渲染各實例之間渲染:
```
<%= render partial: @products, spacer_template: "product_ruler" %>
```
在這段代碼中,渲染各 `_product` 局部視圖之間還會渲染 `_product_ruler` 局部視圖(不傳入任何數據)。
#### 3.3 布局
布局用來渲染 Rails 控制器動作的頁面整體結構。一般來說,Rails 程序中有多個布局,大多數頁面都使用這個布局渲染。例如,網站中可能有個布局用來渲染用戶登錄后的頁面,以及一個布局用來渲染市場和銷售頁面。在用戶登錄后使用的布局中可能包含一個頂級導航,會在多個控制器動作中使用。在 SaaS 程序中,銷售布局中可能包含一個頂級導航,指向“定價”和“聯系”頁面。每個布局都可以有自己的外觀樣式。關于布局的詳細介紹,請閱讀“[Rails 布局和視圖渲染](layouts_and_rendering.html)”一文。
### 4 局部布局
局部視圖可以使用自己的布局。局部布局和動作使用的全局布局不一樣,但原理相同。
例如,要在網頁中顯示一篇文章,文章包含在一個 `div` 標簽中。首先,我們要創建一個新 `Post` 實例:
```
Post.create(body: 'Partial Layouts are cool!')
```
在 `show` 動作的視圖中,我們要在 `box` 布局中渲染 `_post` 局部視圖:
```
<%= render partial: 'post', layout: 'box', locals: {post: @post} %>
```
`box` 布局只是把 `_post` 局部視圖放在一個 `div` 標簽中:
```
<div class='box'>
<%= yield %>
</div>
```
在 `_post` 局部視圖中,文章的內容放在一個 `div` 標簽中,并設置了標簽的 `id` 屬性,這兩個操作通過 `div_for` 幫助方法實現:
```
<%= div_for(post) do %>
<p><%= post.body %></p>
<% end %>
```
最終渲染的文章如下:
```
<div class='box'>
<div id='post_1'>
<p>Partial Layouts are cool!</p>
</div>
</div>
```
注意,在局部布局中可以使用傳入 `render` 方法的本地變量 `post`。和全局布局不一樣,局部布局文件名前也要加上下劃線。
在局部布局中可以不調用 `yield` 方法,直接使用代碼塊。例如,如果不使用 `_post` 局部視圖,可以這么寫:
```
<% render(layout: 'box', locals: {post: @post}) do %>
<%= div_for(post) do %>
<p><%= post.body %></p>
<% end %>
<% end %>
```
假如還使用相同的 `_box` 局部布局,上述代碼得到的輸出和前面一樣。
### 5 視圖路徑
暫無內容。
### 6 Action View 提供的幫助方法簡介
本節并未列出所有幫助方法。完整的幫助方法列表請查閱 [API 文檔](http://api.rubyonrails.org/classes/ActionView/Helpers.html)。
以下各節對 Action View 提供的幫助方法做個簡單介紹。如果想深入了解各幫助方法,建議查看 [API 文檔](http://api.rubyonrails.org/classes/ActionView/Helpers.html)。
#### 6.1 `RecordTagHelper`
這個模塊提供的幫助方法用來生成記錄的容器標簽,例如 `div`。渲染 Active Record 對象時,如果要將其放入容器標簽中,推薦使用這些幫助方法,因為會相應的設置標簽的 `class` 和 `id` 屬性。如果遵守約定,可以很容易的引用這些容器,不用再想容器的 `class` 或 `id` 屬性值是什么。
##### 6.1.1 `content_tag_for`
為 Active Record 對象生成一個容器標簽。
假設 `@post` 是 `Post` 類的一個對象,可以這么寫:
```
<%= content_tag_for(:tr, @post) do %>
<td><%= @post.title %></td>
<% end %>
```
生成的 HTML 如下:
```
<tr id="post_1234" class="post">
<td>Hello World!</td>
</tr>
```
還可以使用一個 Hash 指定 HTML 屬性,例如:
```
<%= content_tag_for(:tr, @post, class: "frontpage") do %>
<td><%= @post.title %></td>
<% end %>
```
生成的 HTML 如下:
```
<tr id="post_1234" class="post frontpage">
<td>Hello World!</td>
</tr>
```
還可傳入 Active Record 對象集合,`content_tag_for` 方法會遍歷集合,為每個元素生成一個容器標簽。假如 `@posts` 中有兩個 `Post` 對象:
```
<%= content_tag_for(:tr, @posts) do |post| %>
<td><%= post.title %></td>
<% end %>
```
生成的 HTML 如下:
```
<tr id="post_1234" class="post">
<td>Hello World!</td>
</tr>
<tr id="post_1235" class="post">
<td>Ruby on Rails Rocks!</td>
</tr>
```
##### 6.1.2 `div_for`
這個方法是使用 `content_tag_for` 創建 `div` 標簽的快捷方式。可以傳入一個 Active Record 對象,或對象集合。例如:
```
<%= div_for(@post, class: "frontpage") do %>
<td><%= @post.title %></td>
<% end %>
```
生成的 HTML 如下:
```
<div id="post_1234" class="post frontpage">
<td>Hello World!</td>
</div>
```
#### 6.2 `AssetTagHelper`
這個模塊中的幫助方法用來生成鏈接到靜態資源文件的 HTML,例如圖片、JavaScript 文件、樣式表和 Feed 等。
默認情況下,Rails 鏈接的靜態文件在程序所處主機的 `public` 文件夾中。不過也可以鏈接到靜態資源文件專用的服務器,在程序的設置文件(一般來說是 `config/environments/production.rb`)中設置 `config.action_controller.asset_host` 選項即可。假設靜態資源服務器是 `assets.example.com`:
```
config.action_controller.asset_host = "assets.example.com"
image_tag("rails.png") # => <img src="http://assets.example.com/images/rails.png" alt="Rails" />
```
##### 6.2.1 `register_javascript_expansion`
這個方法注冊一到多個 JavaScript 文件,把 Symbol 傳給 `javascript_include_tag` 方法時,會引入相應的文件。這個方法經常用在插件的初始化代碼中,注冊保存在 `vendor/assets/javascripts` 文件夾中的 JavaScript 文件。
```
ActionView::Helpers::AssetTagHelper.register_javascript_expansion monkey: ["head", "body", "tail"]
javascript_include_tag :monkey # =>
<script src="/assets/head.js"></script>
<script src="/assets/body.js"></script>
<script src="/assets/tail.js"></script>
```
##### 6.2.2 `register_stylesheet_expansion`
這個方法注冊一到多個樣式表文件,把 Symbol 傳給 `stylesheet_link_tag` 方法時,會引入相應的文件。這個方法經常用在插件的初始化代碼中,注冊保存在 `vendor/assets/stylesheets` 文件夾中的樣式表文件。
```
ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion monkey: ["head", "body", "tail"]
stylesheet_link_tag :monkey # =>
<link href="/assets/head.css" media="screen" rel="stylesheet" />
<link href="/assets/body.css" media="screen" rel="stylesheet" />
<link href="/assets/tail.css" media="screen" rel="stylesheet" />
```
##### 6.2.3 `auto_discovery_link_tag`
返回一個 `link` 標簽,瀏覽器和 Feed 閱讀器用來自動檢測 RSS 或 Atom Feed。
```
auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "RSS Feed"}) # =>
<link rel="alternate" type="application/rss+xml" title="RSS Feed" href="http://www.example.com/feed" />
```
##### 6.2.4 `image_path`
生成 `app/assets/images` 文件夾中所存圖片的地址。得到的地址是從根目錄到圖片的完整路徑。用于 `image_tag` 方法,獲取圖片的路徑。
```
image_path("edit.png") # => /assets/edit.png
```
如果 `config.assets.digest` 選項為 `true`,圖片文件名后會加上指紋碼。
```
image_path("edit.png") # => /assets/edit-2d1a2db63fc738690021fedb5a65b68e.png
```
##### 6.2.5 `image_url`
生成 `app/assets/images` 文件夾中所存圖片的 URL 地址。`image_url` 會調用 `image_path`,然后加上程序的主機地址或靜態文件的主機地址。
```
image_url("edit.png") # => http://www.example.com/assets/edit.png
```
##### 6.2.6 `image_tag`
生成圖片的 HTML `image` 標簽。圖片的地址可以是完整的 URL,或者 `app/assets/images` 文件夾中的圖片。
```
image_tag("icon.png") # => <img src="/assets/icon.png" alt="Icon" />
```
##### 6.2.7 `javascript_include_tag`
為指定的每個資源生成 HTML `script` 標簽。可以傳入 `app/assets/javascripts` 文件夾中所存 JavaScript 文件的文件名(擴展名 `.js` 可加可不加),或者可以使用相對文件根目錄的完整路徑。
```
javascript_include_tag "common" # => <script src="/assets/common.js"></script>
```
如果程序不使用 Asset Pipeline,要想引入 jQuery,可以傳入 `:default`。使用 `:default` 時,如果 `app/assets/javascripts` 文件夾中存在 `application.js` 文件,也會將其引入。
```
javascript_include_tag :defaults
```
還可以使用 `:all` 引入 `app/assets/javascripts` 文件夾中所有的 JavaScript 文件。
```
javascript_include_tag :all
```
多個 JavaScript 文件還可合并成一個文件,減少 HTTP 連接數,還可以使用 gzip 壓縮(提升傳輸速度)。只有 `ActionController::Base.perform_caching` 為 `true`(生產環境的默認值,開發環境為 `false`)時才會合并文件。
```
javascript_include_tag :all, cache: true # =>
<script src="/javascripts/all.js"></script>
```
##### 6.2.8 `javascript_path`
生成 `app/assets/javascripts` 文件夾中 JavaScript 文件的地址。如果沒指定文件的擴展名,會自動加上 `.js`。參數也可以使用相對文檔根路徑的完整地址。這個方法在 `javascript_include_tag` 中調用,用來生成腳本的地址。
```
javascript_path "common" # => /assets/common.js
```
##### 6.2.9 `javascript_url`
生成 `app/assets/javascripts` 文件夾中 JavaScript 文件的 URL 地址。這個方法調用 `javascript_path`,然后再加上當前程序的主機地址或靜態資源文件的主機地址。
```
javascript_url "common" # => http://www.example.com/assets/common.js
```
##### 6.2.10 `stylesheet_link_tag`
返回指定資源的樣式表 `link` 標簽。如果沒提供擴展名,會自動加上 `.css`。
```
stylesheet_link_tag "application" # => <link href="/assets/application.css" media="screen" rel="stylesheet" />
```
還可以使用 `:all`,引入 `app/assets/stylesheets` 文件夾中的所有樣式表。
```
stylesheet_link_tag :all
```
多個樣式表還可合并成一個文件,減少 HTTP 連接數,還可以使用 gzip 壓縮(提升傳輸速度)。只有 `ActionController::Base.perform_caching` 為 `true`(生產環境的默認值,開發環境為 `false`)時才會合并文件。
```
stylesheet_link_tag :all, cache: true
# => <link href="/assets/all.css" media="screen" rel="stylesheet" />
```
##### 6.2.11 `stylesheet_path`
生成 `app/assets/stylesheets` 文件夾中樣式表的地址。如果沒指定文件的擴展名,會自動加上 `.css`。參數也可以使用相對文檔根路徑的完整地址。這個方法在 `stylesheet_link_tag` 中調用,用來生成樣式表的地址。
```
stylesheet_path "application" # => /assets/application.css
```
##### 6.2.12 `stylesheet_url`
生成 `app/assets/stylesheets` 文件夾中樣式表的 URL 地址。這個方法調用 `stylesheet_path`,然后再加上當前程序的主機地址或靜態資源文件的主機地址。
```
stylesheet_url "application" # => http://www.example.com/assets/application.css
```
#### 6.3 `AtomFeedHelper`
##### 6.3.1 `atom_feed`
這個幫助方法可以簡化生成 Atom Feed 的過程。下面是個完整的示例:
```
resources :posts
```
```
def index
@posts = Post.all
respond_to do |format|
format.html
format.atom
end
end
```
```
atom_feed do |feed|
feed.title("Posts Index")
feed.updated((@posts.first.created_at))
@posts.each do |post|
feed.entry(post) do |entry|
entry.title(post.title)
entry.content(post.body, type: 'html')
entry.author do |author|
author.name(post.author_name)
end
end
end
end
```
#### 6.4 `BenchmarkHelper`
##### 6.4.1 `benchmark`
這個方法可以計算模板中某個代碼塊的執行時間,然后把結果寫入日志。可以把耗時的操作或瓶頸操作放入 `benchmark` 代碼塊中,查看此項操作使用的時間。
```
<% benchmark "Process data files" do %>
<%= expensive_files_operation %>
<% end %>
```
上述代碼會在日志中寫入類似“Process data files (0.34523)”的文本,可用來對比優化前后的時間。
#### 6.5 `CacheHelper`
##### 6.5.1 `cache`
這個方法緩存視圖片段,而不是整個動作或頁面。常用來緩存目錄,新話題列表,靜態 HTML 片段等。此方法接受一個代碼塊,即要緩存的內容。詳情參見 `ActionController::Caching::Fragments` 模塊的文檔。
```
<% cache do %>
<%= render "shared/footer" %>
<% end %>
```
#### 6.6 `CaptureHelper`
##### 6.6.1 `capture`
`capture` 方法可以把視圖中的一段代碼賦值給一個變量,這個變量可以在任何模板或視圖中使用。
```
<% @greeting = capture do %>
<p>Welcome! The date and time is <%= Time.now %></p>
<% end %>
```
`@greeting` 變量可以在任何地方使用。
```
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<%= @greeting %>
</body>
</html>
```
##### 6.6.2 `content_for`
`content_for` 方法用一個標記符表示一段代碼,在其他模板或布局中,可以把這個標記符傳給 `yield` 方法,調用這段代碼。
例如,程序有個通用的布局,但還有一個特殊頁面,用到了其他頁面不需要的 JavaScript 文件,此時就可以在這個特殊的頁面中使用 `content_for` 方法,在不影響其他頁面的情況下,引入所需的 JavaScript。
```
<html>
<head>
<title>Welcome!</title>
<%= yield :special_script %>
</head>
<body>
<p>Welcome! The date and time is <%= Time.now %></p>
</body>
</html>
```
```
<p>This is a special page.</p>
<% content_for :special_script do %>
<script>alert('Hello!')</script>
<% end %>
```
#### 6.7 `DateHelper`
##### 6.7.1 `date_select`
這個方法會生成一組選擇列表,分別對應年月日,用來設置日期相關的屬性。
```
date_select("post", "published_on")
```
##### 6.7.2 `datetime_select`
這個方法會生成一組選擇列表,分別對應年月日時分,用來設置日期和時間相關的屬性。
```
datetime_select("post", "published_on")
```
##### 6.7.3 `distance_of_time_in_words`
這個方法會計算兩個時間、兩個日期或兩個秒數之間的近似間隔。如果想得到更精準的間隔,可以把 `include_seconds` 選項設為 `true`。
```
distance_of_time_in_words(Time.now, Time.now + 15.seconds) # => less than a minute
distance_of_time_in_words(Time.now, Time.now + 15.seconds, include_seconds: true) # => less than 20 seconds
```
##### 6.7.4 `select_date`
返回一組 HTML 選擇列表標簽,分別對應年月日,并且選中指定的日期。
```
# Generates a date select that defaults to the date provided (six days after today)
select_date(Time.today + 6.days)
# Generates a date select that defaults to today (no specified date)
select_date()
```
##### 6.7.5 `select_datetime`
返回一組 HTML 選擇列表標簽,分別對應年月日時分,并且選中指定的日期和時間。
```
# Generates a datetime select that defaults to the datetime provided (four days after today)
select_datetime(Time.now + 4.days)
# Generates a datetime select that defaults to today (no specified datetime)
select_datetime()
```
##### 6.7.6 `select_day`
返回一個選擇列表標簽,其選項是當前月份的每一天,并且選中當日。
```
# Generates a select field for days that defaults to the day for the date provided
select_day(Time.today + 2.days)
# Generates a select field for days that defaults to the number given
select_day(5)
```
##### 6.7.7 `select_hour`
返回一個選擇列表標簽,其選項是一天中的每一個小時(0-23),并且選中當前的小時數。
```
# Generates a select field for hours that defaults to the hours for the time provided
select_hour(Time.now + 6.hours)
```
##### 6.7.8 `select_minute`
返回一個選擇列表標簽,其選項是一小時中的每一分鐘(0-59),并且選中當前的分鐘數。
```
# Generates a select field for minutes that defaults to the minutes for the time provided.
select_minute(Time.now + 6.hours)
```
##### 6.7.9 `select_month`
返回一個選擇列表標簽,其選項是一年之中的所有月份(“January”-“December”),并且選中當前月份。
```
# Generates a select field for months that defaults to the current month
select_month(Date.today)
```
##### 6.7.10 `select_second`
返回一個選擇列表標簽,其選項是一分鐘內的各秒數(0-59),并且選中當前時間的秒數。
```
# Generates a select field for seconds that defaults to the seconds for the time provided
select_second(Time.now + 16.minutes)
```
##### 6.7.11 `select_time`
返回一組 HTML 選擇列表標簽,分別對應小時和分鐘。
```
# Generates a time select that defaults to the time provided
select_time(Time.now)
```
##### 6.7.12 `select_year`
返回一個選擇列表標簽,其選項是今年前后各五年,并且選擇今年。年份的前后范圍可使用 `:start_year` 和 `:end_year` 選項指定。
```
# Generates a select field for five years on either side of Date.today that defaults to the current year
select_year(Date.today)
# Generates a select field from 1900 to 2009 that defaults to the current year
select_year(Date.today, start_year: 1900, end_year: 2009)
```
##### 6.7.13 `time_ago_in_words`
和 `distance_of_time_in_words` 方法作用類似,但是后一個時間點固定為當前時間(`Time.now`)。
```
time_ago_in_words(3.minutes.from_now) # => 3 minutes
```
##### 6.7.14 `time_select`
返回一組選擇列表標簽,分別對應小時和分鐘,秒數是可選的,用來設置基于時間的屬性。選中的值會作為多個參數賦值給 Active Record 對象。
```
# Creates a time select tag that, when POSTed, will be stored in the order variable in the submitted attribute
time_select("order", "submitted")
```
#### 6.8 `DebugHelper`
返回一個 `pre` 標簽,以 YAML 格式顯示對象。用這種方法審查對象,可讀性極高。
```
my_hash = {'first' => 1, 'second' => 'two', 'third' => [1,2,3]}
debug(my_hash)
```
```
<pre class='debug_dump'>---
first: 1
second: two
third:
- 1
- 2
- 3
</pre>
```
#### 6.9 `FormHelper`
表單幫助方法的目的是替代標準的 HTML 元素,簡化處理模型的過程。`FormHelper` 模塊提供了很多方法,基于模型創建表單,不單可以生成表單的 HTML 標簽,還能生成各種輸入框標簽,例如文本輸入框,密碼輸入框,選擇列表等。提交表單后(用戶點擊提交按鈕,或者在 JavaScript 中調用 `form.submit`),其輸入框中的值會存入 `params` 對象,傳給控制器。
表單幫助方法分為兩類,一種專門處理模型,另一種則不是。前者處理模型的屬性;后者不處理模型屬性,詳情參見 `ActionView::Helpers::FormTagHelper` 模塊的文檔。
`FormHelper` 模塊的核心是 `form_for` 方法,生成處理模型實例的表單。例如,有個名為 `Person` 的模型,要創建一個新實例,可使用下面的代碼實現:
```
# Note: a @person variable will have been created in the controller (e.g. @person = Person.new)
<%= form_for @person, url: {action: "create"} do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= submit_tag 'Create' %>
<% end %>
```
生成的 HTML 如下:
```
<form action="/people/create" method="post">
<input id="person_first_name" name="person[first_name]" type="text" />
<input id="person_last_name" name="person[last_name]" type="text" />
<input name="commit" type="submit" value="Create" />
</form>
```
表單提交后創建的 `params` 對象如下:
```
{"action" => "create", "controller" => "people", "person" => {"first_name" => "William", "last_name" => "Smith"}}
```
`params` 中有個嵌套 Hash `person`,在控制器中使用 `params[:person]` 獲取。
##### 6.9.1 `check_box`
返回一個復選框標簽,處理指定的屬性。
```
# Let's say that @post.validated? is 1:
check_box("post", "validated")
# => <input type="checkbox" id="post_validated" name="post[validated]" value="1" />
# <input name="post[validated]" type="hidden" value="0" />
```
##### 6.9.2 `fields_for`
類似 `form_for`,為指定的模型創建一個作用域,但不會生成 `form` 標簽。特別適合在同一個表單中處理多個模型。
```
<%= form_for @person, url: {action: "update"} do |person_form| %>
First name: <%= person_form.text_field :first_name %>
Last name : <%= person_form.text_field :last_name %>
<%= fields_for @person.permission do |permission_fields| %>
Admin? : <%= permission_fields.check_box :admin %>
<% end %>
<% end %>
```
##### 6.9.3 `file_field`
返回一個文件上傳輸入框,處理指定的屬性。
```
file_field(:user, :avatar)
# => <input type="file" id="user_avatar" name="user[avatar]" />
```
##### 6.9.4 `form_for`
為指定的模型創建一個表單和作用域,表單中各字段的值都通過這個模型獲取。
```
<%= form_for @post do |f| %>
<%= f.label :title, 'Title' %>:
<%= f.text_field :title %><br>
<%= f.label :body, 'Body' %>:
<%= f.text_area :body %><br>
<% end %>
```
##### 6.9.5 `hidden_field`
返回一個隱藏 `input` 標簽,處理指定的屬性。
```
hidden_field(:user, :token)
# => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
```
##### 6.9.6 `label`
返回一個 `label` 標簽,為指定屬性的輸入框加上標簽。
```
label(:post, :title)
# => <label for="post_title">Title</label>
```
##### 6.9.7 `password_field`
返回一個密碼輸入框,處理指定的屬性。
```
password_field(:login, :pass)
# => <input type="text" id="login_pass" name="login[pass]" value="#{@login.pass}" />
```
##### 6.9.8 `radio_button`
返回一個單選框,處理指定的屬性。
```
# Let's say that @post.category returns "rails":
radio_button("post", "category", "rails")
radio_button("post", "category", "java")
# => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
# <input type="radio" id="post_category_java" name="post[category]" value="java" />
```
##### 6.9.9 `text_area`
返回一個多行文本輸入框,處理指定的屬性。
```
text_area(:comment, :text, size: "20x30")
# => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
# #{@comment.text}
# </textarea>
```
##### 6.9.10 `text_field`
返回一個文本輸入框,處理指定的屬性。
```
text_field(:post, :title)
# => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" />
```
##### 6.9.11 `email_field`
返回一個 Email 輸入框,處理指定的屬性。
```
email_field(:user, :email)
# => <input type="email" id="user_email" name="user[email]" value="#{@user.email}" />
```
##### 6.9.12 `url_field`
返回一個 URL 輸入框,處理指定的屬性。
```
url_field(:user, :url)
# => <input type="url" id="user_url" name="user[url]" value="#{@user.url}" />
```
#### 6.10 `FormOptionsHelper`
這個模塊提供很多方法用來把不同類型的集合轉換成一組 `option` 標簽。
##### 6.10.1 `collection_select`
為 `object` 類的 `method` 方法返回的集合創建 `select` 和 `option` 標簽。
使用此方法的模型示例:
```
class Post < ActiveRecord::Base
belongs_to :author
end
class Author < ActiveRecord::Base
has_many :posts
def name_with_initial
"#{first_name.first}. #{last_name}"
end
end
```
使用舉例,為文章實例(`@post`)選擇作者(`Author`):
```
collection_select(:post, :author_id, Author.all, :id, :name_with_initial, {prompt: true})
```
如果 `@post.author_id` 的值是 1,上述代碼生成的 HTML 如下:
```
<select name="post[author_id]">
<option value="">Please select</option>
<option value="1" selected="selected">D. Heinemeier Hansson</option>
<option value="2">D. Thomas</option>
<option value="3">M. Clark</option>
</select>
```
##### 6.10.2 `collection_radio_buttons`
為 `object` 類的 `method` 方法返回的集合創建 `radio_button` 標簽。
使用此方法的模型示例:
```
class Post < ActiveRecord::Base
belongs_to :author
end
class Author < ActiveRecord::Base
has_many :posts
def name_with_initial
"#{first_name.first}. #{last_name}"
end
end
```
使用舉例,為文章實例(`@post`)選擇作者(`Author`):
```
collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial)
```
如果 `@post.author_id` 的值是 1,上述代碼生成的 HTML 如下:
```
<input id="post_author_id_1" name="post[author_id]" type="radio" value="1" checked="checked" />
<label for="post_author_id_1">D. Heinemeier Hansson</label>
<input id="post_author_id_2" name="post[author_id]" type="radio" value="2" />
<label for="post_author_id_2">D. Thomas</label>
<input id="post_author_id_3" name="post[author_id]" type="radio" value="3" />
<label for="post_author_id_3">M. Clark</label>
```
##### 6.10.3 `collection_check_boxes`
為 `object` 類的 `method` 方法返回的集合創建復選框標簽。
使用此方法的模型示例:
```
class Post < ActiveRecord::Base
has_and_belongs_to_many :authors
end
class Author < ActiveRecord::Base
has_and_belongs_to_many :posts
def name_with_initial
"#{first_name.first}. #{last_name}"
end
end
```
使用舉例,為文章實例(`@post`)選擇作者(`Author`):
```
collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial)
```
如果 `@post.author_ids` 的值是 `[1]`,上述代碼生成的 HTML 如下:
```
<input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" checked="checked" />
<label for="post_author_ids_1">D. Heinemeier Hansson</label>
<input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" />
<label for="post_author_ids_2">D. Thomas</label>
<input id="post_author_ids_3" name="post[author_ids][]" type="checkbox" value="3" />
<label for="post_author_ids_3">M. Clark</label>
<input name="post[author_ids][]" type="hidden" value="" />
```
##### 6.10.4 `country_options_for_select`
返回一組 `option` 標簽,幾乎包含世界上所有國家。
##### 6.10.5 `country_select`
返回指定對象和方法的 `select` 和 `option` 標簽。使用 `country_options_for_select` 方法生成各個 `option` 標簽。
##### 6.10.6 `option_groups_from_collection_for_select`
返回一個字符串,由多個 `option` 標簽組成。和 `options_from_collection_for_select` 方法類似,但會根據對象之間的關系使用 `optgroup` 標簽分組。
使用此方法的模型示例:
```
class Continent < ActiveRecord::Base
has_many :countries
# attribs: id, name
end
class Country < ActiveRecord::Base
belongs_to :continent
# attribs: id, name, continent_id
end
```
使用舉例:
```
option_groups_from_collection_for_select(@continents, :countries, :name, :id, :name, 3)
```
可能得到的輸出如下:
```
<optgroup label="Africa">
<option value="1">Egypt</option>
<option value="4">Rwanda</option>
...
</optgroup>
<optgroup label="Asia">
<option value="3" selected="selected">China</option>
<option value="12">India</option>
<option value="5">Japan</option>
...
</optgroup>
```
注意,這個方法只會返回 `optgroup` 和 `option` 標簽,所以你要把輸出放入 `select` 標簽中。
##### 6.10.7 `options_for_select`
接受一個集合(Hash,數組,可枚舉的對象等),返回一個由 `option` 標簽組成的字符串。
```
options_for_select([ "VISA", "MasterCard" ])
# => <option>VISA</option> <option>MasterCard</option>
```
注意,這個方法只返回 `option` 標簽,所以你要把輸出放入 `select` 標簽中。
##### 6.10.8 `options_from_collection_for_select`
遍歷 `collection`,返回一組 `option` 標簽。每個 `option` 標簽的值是在 `collection` 元素上調用 `value_method` 方法得到的結果,`option` 標簽的顯示文本是在 `collection` 元素上調用 `text_method` 方法得到的結果
```
# options_from_collection_for_select(collection, value_method, text_method, selected = nil)
```
例如,下面的代碼遍歷 `@project.people`,生成一組 `option` 標簽:
```
options_from_collection_for_select(@project.people, "id", "name")
# => <option value="#{person.id}">#{person.name}</option>
```
注意:`options_from_collection_for_select` 方法只返回 `option` 標簽,你應該將其放在 `select` 標簽中。
##### 6.10.9 `select`
創建一個 `select` 元素以及根據指定對象和方法得到的一系列 `option` 標簽。
例如:
```
select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {include_blank: true})
```
如果 `@post.person_id` 的值為 1,返回的結果是:
```
<select name="post[person_id]">
<option value=""></option>
<option value="1" selected="selected">David</option>
<option value="2">Sam</option>
<option value="3">Tobias</option>
</select>
```
##### 6.10.10 `time_zone_options_for_select`
返回一組 `option` 標簽,包含幾乎世界上所有的時區。
##### 6.10.11 `time_zone_select`
為指定的對象和方法返回 `select` 標簽和 `option` 標簽,`option` 標簽使用 `time_zone_options_for_select` 方法生成。
```
time_zone_select( "user", "time_zone")
```
##### 6.10.12 `date_field`
返回一個 `date` 類型的 `input` 標簽,用于訪問指定的屬性。
```
date_field("user", "dob")
```
#### 6.11 `FormTagHelper`
這個模塊提供一系列方法用于創建表單標簽。`FormHelper` 依賴于傳入模板的 Active Record 對象,但 `FormTagHelper` 需要手動指定標簽的 `name` 屬性和 `value` 屬性。
##### 6.11.1 `check_box_tag`
為表單創建一個復選框標簽。
```
check_box_tag 'accept'
# => <input id="accept" name="accept" type="checkbox" value="1" />
```
##### 6.11.2 `field_set_tag`
創建 `fieldset` 標簽,用于分組 HTML 表單元素。
```
<%= field_set_tag do %>
<p><%= text_field_tag 'name' %></p>
<% end %>
# => <fieldset><p><input id="name" name="name" type="text" /></p></fieldset>
```
##### 6.11.3 `file_field_tag`
創建一個文件上傳輸入框。
```
<%= form_tag({action:"post"}, multipart: true) do %>
<label for="file">File to Upload</label> <%= file_field_tag "file" %>
<%= submit_tag %>
<% end %>
```
結果示例:
```
file_field_tag 'attachment'
# => <input id="attachment" name="attachment" type="file" />
```
##### 6.11.4 `form_tag`
創建 `form` 標簽,指向的地址由 `url_for_options` 選項指定,和 `ActionController::Base#url_for` 方法類似。
```
<%= form_tag '/posts' do %>
<div><%= submit_tag 'Save' %></div>
<% end %>
# => <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
```
##### 6.11.5 `hidden_field_tag`
為表單創建一個隱藏的 `input` 標簽,用于傳遞由于 HTTP 無狀態的特性而丟失的數據,或者隱藏不想讓用戶看到的數據。
```
hidden_field_tag 'token', 'VUBJKB23UIVI1UU1VOBVI@'
# => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" />
```
##### 6.11.6 `image_submit_tag`
顯示一個圖片,點擊后提交表單。
```
image_submit_tag("login.png")
# => <input src="/images/login.png" type="image" />
```
##### 6.11.7 `label_tag`
創建一個 `label` 標簽。
```
label_tag 'name'
# => <label for="name">Name</label>
```
##### 6.11.8 `password_field_tag`
創建一個密碼輸入框,用戶輸入的值會被遮蓋。
```
password_field_tag 'pass'
# => <input id="pass" name="pass" type="password" />
```
##### 6.11.9 `radio_button_tag`
創建一個單選框。如果希望用戶從一組選項中選擇,可以使用多個單選框,`name` 屬性的值都設為一樣的。
```
radio_button_tag 'gender', 'male'
# => <input id="gender_male" name="gender" type="radio" value="male" />
```
##### 6.11.10 `select_tag`
創建一個下拉選擇框。
```
select_tag "people", "<option>David</option>"
# => <select id="people" name="people"><option>David</option></select>
```
##### 6.11.11 `submit_tag`
創建一個提交按鈕,按鈕上顯示指定的文本。
```
submit_tag "Publish this post"
# => <input name="commit" type="submit" value="Publish this post" />
```
##### 6.11.12 `text_area_tag`
創建一個多行文本輸入框,用于輸入大段文本,例如博客和描述信息。
```
text_area_tag 'post'
# => <textarea id="post" name="post"></textarea>
```
##### 6.11.13 `text_field_tag`
創建一個標準文本輸入框,用于輸入小段文本,例如用戶名和搜索關鍵字。
```
text_field_tag 'name'
# => <input id="name" name="name" type="text" />
```
##### 6.11.14 `email_field_tag`
創建一個標準文本輸入框,用于輸入 Email 地址。
```
email_field_tag 'email'
# => <input id="email" name="email" type="email" />
```
##### 6.11.15 `url_field_tag`
創建一個標準文本輸入框,用于輸入 URL 地址。
```
url_field_tag 'url'
# => <input id="url" name="url" type="url" />
```
##### 6.11.16 `date_field_tag`
創建一個標準文本輸入框,用于輸入日期。
```
date_field_tag "dob"
# => <input id="dob" name="dob" type="date" />
```
#### 6.12 `JavaScriptHelper`
這個模塊提供在視圖中使用 JavaScript 的相關方法。
##### 6.12.1 `button_to_function`
返回一個按鈕,點擊后觸發一個 JavaScript 函數。例如:
```
button_to_function "Greeting", "alert('Hello world!')"
button_to_function "Delete", "if (confirm('Really?')) do_delete()"
button_to_function "Details" do |page|
page[:details].visual_effect :toggle_slide
end
```
##### 6.12.2 `define_javascript_functions`
在一個 `script` 標簽中引入 Action Pack JavaScript 代碼庫。
##### 6.12.3 `escape_javascript`
轉義 JavaScript 中的回車符、單引號和雙引號。
##### 6.12.4 `javascript_tag`
返回一個 `script` 標簽,把指定的代碼放入其中。
```
javascript_tag "alert('All is good')"
```
```
<script>
//<![CDATA[
alert('All is good')
//]]>
</script>
```
##### 6.12.5 `link_to_function`
返回一個鏈接,點擊后觸發指定的 JavaScript 函數并返回 `false`。
```
link_to_function "Greeting", "alert('Hello world!')"
# => <a onclick="alert('Hello world!'); return false;" href="#">Greeting</a>
```
#### 6.13 `NumberHelper`
這個模塊提供用于把數字轉換成格式化字符串所需的方法。包括用于格式化電話號碼、貨幣、百分比、精度、進位制和文件大小的方法。
##### 6.13.1 `number_to_currency`
把數字格式化成貨幣字符串,例如 $13.65。
```
number_to_currency(1234567890.50) # => $1,234,567,890.50
```
##### 6.13.2 `number_to_human_size`
把字節數格式化成更易理解的形式,顯示文件大小時特別有用。
```
number_to_human_size(1234) # => 1.2 KB
number_to_human_size(1234567) # => 1.2 MB
```
##### 6.13.3 `number_to_percentage`
把數字格式化成百分數形式。
```
number_to_percentage(100, precision: 0) # => 100%
```
##### 6.13.4 `number_to_phone`
把數字格式化成美國使用的電話號碼形式。
```
number_to_phone(1235551234) # => 123-555-1234
```
##### 6.13.5 `number_with_delimiter`
格式化數字,使用分隔符隔開每三位數字。
```
number_with_delimiter(12345678) # => 12,345,678
```
##### 6.13.6 `number_with_precision`
使用指定的精度格式化數字,精度默認值為 3。
```
number_with_precision(111.2345) # => 111.235
number_with_precision(111.2345, 2) # => 111.23
```
#### 6.14 `SanitizeHelper`
`SanitizeHelper` 模塊提供一系列方法,用于剔除不想要的 HTML 元素。
##### 6.14.1 `sanitize`
`sanitize` 方法會編碼所有標簽,并刪除所有不允許使用的屬性。
```
sanitize @article.body
```
如果指定了 `:attributes` 或 `:tags` 選項,只允許使用指定的標簽和屬性。
```
sanitize @article.body, tags: %w(table tr td), attributes: %w(id class style)
```
要想修改默認值,例如允許使用 `table` 標簽,可以這么設置:
```
class Application < Rails::Application
config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
end
```
##### 6.14.2 `sanitize_css(style)`
過濾一段 CSS 代碼。
##### 6.14.3 `strip_links(html)`
刪除文本中的所有鏈接標簽,但保留鏈接文本。
```
strip_links("<a href="http://rubyonrails.org">Ruby on Rails</a>")
# => Ruby on Rails
```
```
strip_links("emails to <a href="mailto:me@email.com">me@email.com</a>.")
# => emails to me@email.com.
```
```
strip_links('Blog: <a href="http://myblog.com/">Visit</a>.')
# => Blog: Visit.
```
##### 6.14.4 `strip_tags(html)`
過濾 `html` 中的所有 HTML 標簽,以及注釋。
這個方法使用 `html-scanner` 解析 HTML,所以解析能力受 `html-scanner` 的限制。
```
strip_tags("Strip <i>these</i> tags!")
# => Strip these tags!
```
```
strip_tags("<b>Bold</b> no more! <a href='more.html'>See more</a>")
# => Bold no more! See more
```
注意,得到的結果中可能仍然有字符 `<`、`>` 和 `&`,會導致瀏覽器顯示異常。
### 7 視圖本地化
Action View 可以根據當前的本地化設置渲染不同的模板。
例如,假設有個 `PostsController`,在其中定義了 `show` 動作。默認情況下,執行這個動作時渲染的是 `app/views/posts/show.html.erb`。如果設置了 `I18n.locale = :de`,渲染的則是 `app/views/posts/show.de.html.erb`。如果本地化對應的模板不存在就使用默認模板。也就是說,沒必要為所有動作編寫本地化視圖,但如果有本地化對應的模板就會使用。
相同的技術還可用在 `public` 文件夾中的錯誤文件上。例如,設置了 `I18n.locale = :de`,并創建了 `public/500.de.html` 和 `public/404.de.html`,就能顯示本地化的錯誤頁面。
Rails 并不限制 `I18n.locale` 選項的值,因此可以根據任意需求顯示不同的內容。假設想讓專業用戶看到不同于普通用戶的頁面,可以在 `app/controllers/application_controller.rb` 中這么設置:
```
before_action :set_expert_locale
def set_expert_locale
I18n.locale = :expert if current_user.expert?
end
```
然后創建只顯示給專業用戶的 `app/views/posts/show.expert.html.erb` 視圖。
詳情參閱“[Rails 國際化 API](i18n.html)”一文。
### 反饋
歡迎幫忙改善指南質量。
如發現任何錯誤,歡迎修正。開始貢獻前,可先行閱讀[貢獻指南:文檔](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation)。
翻譯如有錯誤,深感抱歉,歡迎 [Fork](https://github.com/ruby-china/guides/fork) 修正,或至此處[回報](https://github.com/ruby-china/guides/issues/new)。
文章可能有未完成或過時的內容。請先檢查 [Edge Guides](http://edgeguides.rubyonrails.org) 來確定問題在 master 是否已經修掉了。再上 master 補上缺少的文件。內容參考 [Ruby on Rails 指南準則](ruby_on_rails_guides_guidelines.html)來了解行文風格。
最后,任何關于 Ruby on Rails 文檔的討論,歡迎到 [rubyonrails-docs 郵件群組](http://groups.google.com/group/rubyonrails-docs)。
- Ruby on Rails 指南 (651bba1)
- 入門
- Rails 入門
- 模型
- Active Record 基礎
- Active Record 數據庫遷移
- Active Record 數據驗證
- Active Record 回調
- Active Record 關聯
- Active Record 查詢
- 視圖
- Action View 基礎
- Rails 布局和視圖渲染
- 表單幫助方法
- 控制器
- Action Controller 簡介
- Rails 路由全解
- 深入
- Active Support 核心擴展
- Rails 國際化 API
- Action Mailer 基礎
- Active Job 基礎
- Rails 程序測試指南
- Rails 安全指南
- 調試 Rails 程序
- 設置 Rails 程序
- Rails 命令行
- Rails 緩存簡介
- Asset Pipeline
- 在 Rails 中使用 JavaScript
- 引擎入門
- Rails 應用的初始化過程
- Autoloading and Reloading Constants
- 擴展 Rails
- Rails 插件入門
- Rails on Rack
- 個性化Rails生成器與模板
- Rails應用模版
- 貢獻 Ruby on Rails
- Contributing to Ruby on Rails
- API Documentation Guidelines
- Ruby on Rails Guides Guidelines
- Ruby on Rails 維護方針
- 發布記
- A Guide for Upgrading Ruby on Rails
- Ruby on Rails 4.2 發布記
- Ruby on Rails 4.1 發布記
- Ruby on Rails 4.0 Release Notes
- Ruby on Rails 3.2 Release Notes
- Ruby on Rails 3.1 Release Notes
- Ruby on Rails 3.0 Release Notes
- Ruby on Rails 2.3 Release Notes
- Ruby on Rails 2.2 Release Notes