LoginSignup
0
0

More than 3 years have passed since last update.

【Rails】ActionView::Helpers::CaptureHelper(content_for, yieldについて)

Last updated at Posted at 2020-08-30

はじめに

  • 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

引数 flushtrue ならば、 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

まとめ

  • 大事だと思ったこと。
    1. yieldcontent_for の違い
    2. content_for に引数がある場合とない場合の挙動の違い
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0