はじめに
- content_for
- content_for?
- yield
- provide
あたりの理解を深めたかった。
特に content_for
の挙動について理解を深めたかった。
概要
Rails ActionView::Helpers::CaptureHelperのドキュメントを追っていく。
メソッドは
capture
provide
content_for
content_for?
の4つが紹介されているが、
とりあえず provide
, content_for
, content_for?
についてだけ読む。
content_for(name, content = nil, options = {}, &block)
1. content_for の説明
content_for
は、HTMLのブロックを後で使用できるように識別子に格納します。他のテンプレート、ヘルパーモジュール、またはレイアウトでこの格納されたコンテンツにアクセスするには、識別子を引数としてに渡します。
注: yield
も保存されたコンテンツを取得するのに使われていますが、ヘルパーモジュールで yield
を呼び出しても機能しません。( content_for
は機能します。)
<% content_for :not_authorized do %>
alert('You are not authorized to do that!')
<% end %>
content_for :not_authorized
は、テンプレートの任意の場所で使用できます。
<%= content_for :not_authorized if current_user.nil? %>
これは次と同等です。
<%= yield :not_authorized if current_user.nil? %>
content_for
はヘルパーモジュールでも使用できます。
module StorageHelper
def stored_content
content_for(:storage) || "Your storage is empty"
end
end
このヘルパーは、通常のヘルパーと同じように機能します。
<%= stored_content %>
2. yield の説明
また、レイアウト内に yield
がある場合でも yield
構文を使用することができます。
<%# This is the layout %>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>My Website</title>
<%= yield :script %>
</head>
<body>
<%= yield %>
</body>
</html>
<%# This is our view %>
Please login!
<% content_for :script do %>
<script>alert('You are not authorized to view this page!')</script>
<% end %>
<%= link_to 'Logout', action: 'logout', remote: true %>
<% content_for :script do %>
<%= javascript_include_tag :defaults %>
<% end %>
これにより、JavaScriptのデフォルトのタグがページに配置されます。この手法は、これらのスクリプトをいくつかのビューでのみ使用する場合に役立ちます。
3. content_for の注意点1
content_for
は特定の識別子に対して指定されたブロックを、順番に連結することに注意してください。
<% content_for :navigation do %>
<li><%= link_to 'Home', action: 'index' %></li>
<% end %>
And in another place:
<% content_for :navigation do %>
<li><%= link_to 'Login', action: 'login' %></li>
<% end %>
このコードは別のテンプレートまたはレイアウトで、両方のリンクを順番にレンダリングします。
<ul><%= content_for :navigation %></ul>
4. content_for の注意点2
引数 flush
が true
ならば、 content_for
は与えられたブロックを置き換えます。
<% content_for :navigation do %>
<li><%= link_to 'Home', action: 'index' %></li>
<% end %>
<%# Add some other content, or use a different template: %>
<% content_for :navigation, flush: true do %>
<li><%= link_to 'Login', action: 'login' %></li>
<% end %>
このコードは別のテンプレートまたはレイアウトで、最後のリンクのみをレンダリングします。
<ul><%= content_for :navigation %></ul>
5. 引数に渡せる値について
単純なコンテンツも引数として渡すことができます。
<% content_for :script, javascript_include_tag(:defaults) %>
※ content_for
はキャッシュでは無視されます。そのため、フラグメントキャッシュされる要素には使用しないでください。
↑フラグメントキャッシュについては別記事でまとめたので参照。
追記
content_for
の実装を見ると、引数がなければ表示する、引数があればキャプチャする という風になっているっぽい。
# File actionview/lib/action_view/helpers/capture_helper.rb, line 155
def content_for(name, content = nil, options = {}, &block)
if content || block_given?
if block_given?
options = content if content
content = capture(&block)
end
if content
options[:flush] ? @view_flow.set(name, content) : @view_flow.append(name, content)
end
nil
else
@view_flow.get(name).presence
end
end
Stack overflowでも(ちょっと古いが)言及されてた。
content_for?(name)
content_for?
を使用すると、コンテンツがキャプチャされているかどうかを確認できます。ビューの内容に基づいてレイアウトの一部を異なる方法でレンダリングするのに役立ちます。
<%# This is the layout %>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>My Website</title>
<%= yield :script %>
</head>
<body class="<%= content_for?(:right_col) ? 'two-column' : 'one-column' %>">
<%= yield %>
<%= yield :right_col %>
</body>
</html>
provide(name, content = nil, &block)
content_for
と同じですが、例えば特定のテンプレートをレンダリングするとき、同じバッファに複数回連結したい時に使用したい場合は content_for
を使用し、そうでない場合は provide
を使用し、レイアウトが余分にコンテンツを検索しないようにしてください。
追記
これも content_for
と同じで、引数がなければ表示、あればキャプチャ、という実装っぽい。
# File actionview/lib/action_view/helpers/capture_helper.rb, line 175
def provide(name, content = nil, &block)
content = capture(&block) if block_given?
result = @view_flow.append!(name, content) if content
result unless content
end
まとめ
- 大事だと思ったこと。
-
yield
とcontent_for
の違い -
content_for
に引数がある場合とない場合の挙動の違い
-