v7.1.3.2
更多資訊請瀏覽 rubyonrails.org: 更多 Ruby on Rails

Action View 概觀

閱讀本指南後,您將了解

1 什麼是 Action View?

在 Rails 中,網頁請求是由 Action Controller 和 Action View 處理。通常,Action Controller 負責與資料庫進行通訊,並在必要時執行 CRUD 動作。然後,Action View 負責編譯回應。

Action View 範本使用內嵌在標籤中的 Ruby 和 HTML 撰寫。為了避免範本中出現過多的樣板程式碼,幾個輔助類別會提供表單、日期和字串的共用行為。隨著應用程式的演進,也可以輕鬆地新增新的輔助工具。

Action View 的某些功能與 Active Record 相關,但這並不表示 Action View 相依於 Active Record。Action View 是獨立的套件,可以使用任何類型的 Ruby 函式庫。

2 使用 Action View 與 Rails

對於每個控制器,在 app/views 目錄中有一個關聯的目錄,其中包含組成與該控制器關聯的檢視的範本檔案。這些檔案用於顯示每個控制器動作所產生的檢視。

讓我們來看看 Rails 在使用 scaffold 產生器建立新資源時預設會做什麼

$ bin/rails generate scaffold article
      [...]
      invoke  scaffold_controller
      create    app/controllers/articles_controller.rb
      invoke    erb
      create      app/views/articles
      create      app/views/articles/index.html.erb
      create      app/views/articles/edit.html.erb
      create      app/views/articles/show.html.erb
      create      app/views/articles/new.html.erb
      create      app/views/articles/_form.html.erb
      [...]

Rails 中的檢視有一個命名慣例。通常,檢視會與關聯的控制器動作共用名稱,如上所示。例如,articles_controller.rb 的 index 控制器動作將使用 app/views/articles 目錄中的 index.html.erb 檢視檔案。傳回給客戶端的完整 HTML 是由這個 ERB 檔案、一個包含它的版面範本,以及檢視可能引用的所有部分組成。在本指南中,您將找到關於這三個組件的更詳細文件。

如前所述,最終的 HTML 輸出是由三個 Rails 元素組成:範本部分版面。以下是它們各自的簡要概述。

3 範本

Action View 範本可以用好幾種方式撰寫。如果範本檔案有 .erb 副檔名,它會使用 ERB (嵌入式 Ruby) 和 HTML 的混合。如果範本檔案有 .builder 副檔名,則會使用 Builder::XmlMarkup 函式庫。

Rails 支援多種範本系統,並使用檔案副檔名來區分它們。例如,使用 ERB 範本系統的 HTML 檔案,其檔案副檔名會是 .html.erb

3.1 ERB

在 ERB 範本中,可以使用 <% %><%= %> 標籤來包含 Ruby 程式碼。<% %> 標籤用於執行不傳回任何內容的 Ruby 程式碼,例如條件、迴圈或區塊,而 <%= %> 標籤則用於您要輸出時。

考慮以下用於名稱的迴圈

<h1>Names of all the people</h1>
<% @people.each do |person| %>
  Name: <%= person.name %><br>
<% end %>

迴圈使用常規嵌入標籤 (<% %>) 設定,而名稱則使用輸出嵌入標籤 (<%= %>) 插入。請注意,這不只是使用建議:常規輸出函式(例如 printputs)不會使用 ERB 範本呈現在檢視中。因此,這會是錯誤的

<%# WRONG %>
Hi, Mr. <% puts "Frodo" %>

若要抑制前導和尾隨空白,您可以將 <%- -%><%%> 互換使用。

3.2 Builder

Builder 範本是 ERB 的更具程式化替代方案。它們對於產生 XML 內容特別有用。一個名為 xml 的 XmlMarkup 物件會自動提供給具有 .builder 副檔名的範本。

以下是一些基本範例

xml.em("emphasized")
xml.em { xml.b("emph & bold") }
xml.a("A Link", "href" => "https://rubyonrails.org")
xml.target("name" => "compile", "option" => "fast")

會產生

<em>emphasized</em>
<em><b>emph &amp; bold</b></em>
<a href="https://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.3 Jbuilder

Jbuilder 是由 Rails 團隊維護並包含在預設 Rails Gemfile 中的 gem。它類似於 Builder,但用於產生 JSON,而非 XML。

如果您沒有它,您可以將以下內容新增到您的 Gemfile

gem 'jbuilder'

名為 json 的 Jbuilder 物件會自動提供給具有 .jbuilder 副檔名的範本。

以下是基本範例

json.name("Alex")
json.email("[email protected]")

將產生

{
  "name": "Alex",
  "email": "[email protected]"
}

請參閱 Jbuilder 文件 以取得更多範例和資訊。

3.4 範本快取

預設情況下,Rails 會將每個範本編譯成方法以呈現它。在開發環境中,當您變更範本時,Rails 會檢查檔案的修改時間並重新編譯它。

4 部分

部分範本(通常只稱為「部分」)是將呈現程序分解成更易於管理的區塊的另一種裝置。使用部分,您可以從範本中萃取程式碼片段到個別檔案,並在範本中重複使用它們。

4.1 呈現部分

若要將部分呈現為檢視的一部分,您可以在檢視中使用 render 方法

<%= render "menu" %>

這會在正在呈現的檢視中,於該點呈現名為 _menu.html.erb 的檔案。請注意前導底線字元:部分會以前導底線命名,以將它們與一般檢視區分開來,即使它們在沒有底線的情況下被參照也是如此。即使您從另一個資料夾中提取部分,這仍然成立

<%= render "application/menu" %>

該程式碼會從 app/views/application/_menu.html.erb 中提取部分。

4.2 使用部分簡化檢視

使用部分內容的一種方式是將它們視為子常式的等效項;一種將詳細資訊移出檢視的方式,以便您更容易掌握正在發生的事情。例如,您可能有一個看起來像這樣的檢視

<%= render "application/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 "application/footer" %>

在此,_ad_banner.html.erb_footer.html.erb 部分內容可能包含應用程式中許多頁面共用的內容。當您專注於特定頁面時,您不需要看到這些區段的詳細資訊。

檢視部分內容依賴於與範本和佈局相同的 範本繼承,因此由繼承自 ApplicationController 的控制器呈現的範本可以呈現宣告於 app/views/application 中的檢視部分內容。

除了使用繼承鏈解析部分內容外,控制器也可以使用繼承鏈覆寫預設部分內容。例如,繼承自 ApplicationControllerProductsController 會先搜尋 app/views/products/_ad_banner.html.erb,然後再回退到 app/views/application/_ad_banner.html.erb,來解析對 <%= render "ad_banner" %> 的呼叫。

4.3 使用 locals 選項的 render

在呈現部分內容時,locals: 選項中的每個金鑰都可用作部分內容的局部變數

<%# app/views/products/show.html.erb %>

<%= render partial: "products/product", locals: { product: @product } %>

<%# app/views/products/_product.html.erb %>

<%= tag.div id: dom_id(product) do %>
  <h1><%= product.name %></h1>
<% end %>

如果範本參照未作為 locals: 選項一部分傳遞到檢視的變數,則範本會引發 ActionView::Template::Error

<%# app/views/products/_product.html.erb %>

<%= tag.div id: dom_id(product) do %>
  <h1><%= product.name %></h1>

  <%# => raises ActionView::Template::Error %>
  <% related_products.each do |related_product| %>
    <%# ... %>
  <% end %>
<% end %>

4.4 使用 local_assigns

locals: 選項中的每個金鑰都可用作部分內容的局部變數,透過 local_assigns 輔助方法

<%# app/views/products/show.html.erb %>

<%= render partial: "products/product", locals: { product: @product } %>

<%# app/views/products/_product.html.erb %>

<% local_assigns[:product]          # => "#<Product:0x0000000109ec5d10>" %>
<% local_assigns[:options]          # => nil %>

由於 local_assignsHash,因此它與 Ruby 3.1 的模式比對指派運算子 相容

local_assigns => { product:, **options }
product # => "#<Product:0x0000000109ec5d10>"
options # => {}

當將 :product 以外的金鑰指派到部分內容的局部 Hash 變數時,它們可以分散到輔助方法呼叫中

<%# app/views/products/_product.html.erb %>

<% local_assigns => { product:, **options } %>

<%= tag.div id: dom_id(product), **options do %>
  <h1><%= product.name %></h1>
<% end %>

<%# app/views/products/show.html.erb %>

<%= render "products/product", product: @product, class: "card" %>
<%# => <div id="product_1" class="card">
  #      <h1>A widget</h1>
  #    </div>
%>

模式比對指派也支援變數重新命名

local_assigns => { product: record }
product             # => "#<Product:0x0000000109ec5d10>"
record              # => "#<Product:0x0000000109ec5d10>"
product == record   # => true

由於 local_assigns 會傳回一個 Hash 實例,因此您可以有條件地讀取變數,然後在金鑰不是 locals: 選項的一部分時,退回預設值

<%# app/views/products/_product.html.erb %>

<% local_assigns.fetch(:related_products, []).each do |related_product| %>
  <%# ... %>
<% end %>

將 Ruby 3.1 的模式比對指派與呼叫 Hash#with_defaults 結合使用,可以進行簡潔的部分局部預設變數指派

<%# app/views/products/_product.html.erb %>

<% local_assigns.with_defaults(related_products: []) => { product:, related_products: } %>

<%= tag.div id: dom_id(product) do %>
  <h1><%= product.name %></h1>

  <% related_products.each do |related_product| %>
    <%# ... %>
  <% end %>
<% end %>

4.5 沒有 partiallocals 選項的 render

在上述範例中,render 會採用 2 個選項:partiallocals。但如果這些是您唯一想傳遞的選項,您可以略過使用這些選項。例如,您可以改用

<%= render partial: "product", locals: { product: @product } %>

您也可以執行

<%= render "product", product: @product %>

4.6 asobject 選項

預設情況下,ActionView::Partials::PartialRenderer 的物件會在與範本同名的局部變數中。因此,假設

<%= render partial: "product" %>

_product 部分中,我們會在局部變數 product 中取得 @product,就好像我們寫了

<%= render partial: "product", locals: { product: @product } %>

object 選項可以用來直接指定要呈現在部分中的物件;當範本的物件在其他地方(例如在不同的實例變數或局部變數中)時,這會很有用。

例如,您可以改用

<%= render partial: "product", locals: { product: @item } %>

我們會執行

<%= render partial: "product", object: @item %>

使用 as 選項,我們可以為所述局部變數指定不同的名稱。例如,如果我們希望它變成 item 而不是 product,我們會執行

<%= render partial: "product", object: @item, as: "item" %>

這等於

<%= render partial: "product", locals: { item: @item } %>

4.7 呈現集合

通常,範本需要反覆運算集合,並針對每個元素呈現子範本。此模式已實作為單一方法,會接受陣列並針對陣列中的每個元素呈現部分範本。

因此,用於呈現所有產品的範例

<% @products.each do |product| %>
  <%= render partial: "product", locals: { product: product } %>
<% end %>

可以用單一行重寫

<%= render partial: "product", collection: @products %>

當部分範本以集合呼叫時,部分範本的個別執行個體可透過以部分範本命名的變數存取要呈現的集合成員。在此情況下,部分範本為 _product,且在其中,您可以參考 product 以取得要呈現的集合成員。

您可以使用簡寫語法來呈現集合。假設 @productsProduct 執行個體的集合,您可以簡單撰寫下列內容以產生相同的結果

<%= render @products %>

Rails 會透過查看集合中的模型名稱(在此情況下為 Product)來判斷要使用的部分範本名稱。事實上,您甚至可以使用此簡寫來呈現由不同模型執行個體組成的集合,且 Rails 會為集合的每個成員選擇適當的部分範本。

4.8 間隔範本

您也可以使用 :spacer_template 選項指定要於主要部分範本執行個體之間呈現的第二個部分範本

<%= render partial: @products, spacer_template: "product_ruler" %>

Rails 會在每個 _product 部分範本之間呈現 _product_ruler 部分範本(不會傳遞任何資料給它)。

4.9 嚴格區域變數

預設情況下,範本會接受任何 locals 作為關鍵字引數。若要定義範本接受的 locals,請加入 locals 魔術註解

<%# locals: (message:) -%>
<%= message %>

也可以提供預設值

<%# locals: (message: "Hello, world!") -%>
<%= message %>

或者可以完全停用 locals

<%# locals: () %>

5 版面配置

版面配置可讓您在 Rails 控制器動作的結果周圍呈現共用檢視範本。通常,Rails 應用程式會有一些版面配置,用於呈現頁面。例如,網站可能有一個版面配置供已登入使用者使用,另一個供網站的行銷或銷售面使用。已登入使用者的版面配置可能包含頂層導覽,應出現在許多控制器動作中。SaaS 應用程式的銷售版面配置可能包含「定價」和「聯絡我們」頁面等頂層導覽。您會希望每個版面配置都有不同的視覺和風格。您可以在 Rails 中的版面配置和呈現 指南中更詳細地了解版面配置。

5.1 部分版面配置

部分版面配置可以套用自己的版面配置。這些版面配置不同於套用至控制器動作的版面配置,但其運作方式類似。

假設我們在頁面上顯示一篇文章,該篇文章應包含在 div 中以供顯示。首先,我們將建立一個新的 Article

Article.create(body: 'Partial Layouts are cool!')

show 範本中,我們將呈現包含在 box 版面配置中的 _article 部分版面配置

articles/show.html.erb

<%= render partial: 'article', layout: 'box', locals: { article: @article } %>

box 版面配置僅將 _article 部分版面配置包含在 div

articles/_box.html.erb

<div class='box'>
  <%= yield %>
</div>

請注意,部分版面配置可以存取傳遞至 render 呼叫的本地 article 變數。不過,與應用程式範圍的版面配置不同,部分版面配置仍有底線前綴。

您也可以在部分版面中呈現程式碼區塊,而不是呼叫yield。例如,如果我們沒有_article部分版面,我們可以這樣做

articles/show.html.erb

<% render(layout: 'box', locals: { article: @article }) do %>
  <div>
    <p><%= article.body %></p>
  </div>
<% end %>

假設我們使用上面相同的_box部分版面,這會產生與前一個範例相同的輸出。

6 檢視路徑

在呈現回應時,控制器需要解析不同檢視的位置。預設情況下,它只會在app/views目錄中尋找。

我們可以使用prepend_view_pathappend_view_path方法新增其他位置,並在解析路徑時給予它們一定的優先權。

6.1 預先新增檢視路徑

例如,當我們想要將檢視放入子網域的不同目錄中時,這會很有用。

我們可以使用

prepend_view_path "app/views/#{request.subdomain}"

來做到這一點。然後,Action View 會在解析檢視時首先查看這個目錄。

6.2 附加檢視路徑

類似地,我們可以附加路徑

append_view_path "app/views/direct"

這會將app/views/direct新增到查詢路徑的結尾。

7 輔助程式

Rails 提供許多輔助程式方法可與 Action View 搭配使用。這些方法包括

  • 格式化日期、字串和數字
  • 建立連結到影像、影片、樣式表等 HTML 連結...
  • 清除內容
  • 建立表單
  • 在地化內容

您可以在Action View 輔助程式指南Action View 表單輔助程式指南中進一步瞭解輔助程式。

8 在地化檢視

Action View 有能力根據目前的語言環境呈現不同的範本。

例如,假設您有一個具有顯示動作的ArticlesController。預設情況下,呼叫這個動作會呈現app/views/articles/show.html.erb。但是,如果您設定I18n.locale = :de,則會改為呈現app/views/articles/show.de.html.erb。如果沒有在地化的範本,則會使用未裝飾的版本。這表示您不需要為所有情況提供在地化的檢視,但如果有的話,它們會優先使用。

您可以使用相同的技術來在地化您的公開目錄中的救援檔案。例如,設定 I18n.locale = :de 並建立 public/500.de.htmlpublic/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/articles/show.expert.html.erb,它只會顯示給專家使用者。

您可以在 這裡 閱讀更多有關 Rails 國際化 (I18n) API 的資訊。

回饋

我們鼓勵您協助提升本指南的品質。

如果您看到任何錯字或事實錯誤,請協助我們修正。若要開始,您可以閱讀我們的 文件貢獻 部分。

您也可能會發現不完整或過時的內容。請務必新增任何主體遺漏的文件。請務必先查看 Edge Guides,以驗證問題是否已在主分支中修正。查看 Ruby on Rails 指南指南 以了解樣式和慣例。

如果您因為任何原因發現需要修正的地方,但無法自行修補,請 開啟問題

最後但並非最不重要的一點是,我們非常歡迎在 官方 Ruby on Rails 論壇 上針對 Ruby on Rails 文件進行任何類型的討論。