Ruby on Rails: provide vs content_for
Asked Answered
B

2

30

I came across the view helper function "provide" today. By looking into its manual I am still confused on how it is different from "content_for".

provide(name, content = nil, &block)

The same as content_for but when used with streaming flushes straight back to the layout. In other words, if you want to concatenate several times to the same buffer when rendering a given template, you should use content_for, if not, use provide to tell the layout to stop looking for more contents.

Question 1: this is quite abstract to me - could anyone flesh it out by giving a demonstrative example?

Question 2: working with asset pipeline, which performs better and why?

Thanks!

Brottman answered 7/1, 2015 at 7:34 Comment(4)
Are you familiar with api.rubyonrails.org/classes/ActionController/Streaming.html ?Antidisestablishmentarianism
Honestly, no before you mention it. I just took a look at the ref you shared, and it is excellent. Never knew that by default Rails load template before layout! So streaming is the way to revert this order. Then..?Brottman
That's just how it's implemented, the main advantage of streaming is to enable parts of the page (especially header JS) to be streamed back to the browser when it's ready, rather than waiting for the whole page.Antidisestablishmentarianism
It sounds better. But what then does the "buffer" there mean? "...concatenate several times to the same buffer when rendering a given template..." It would also be great if you can write it in the answer so I can vote you up or accept the answer to reward you. Thanks!Brottman
M
28

First of all, what is streaming? Why would you use it?

Streaming is alternate method of rendering pages top-down (outside-in). The default rendering behavior is inside-out. Streaming must be enabled in your controller:

class MyController
  def action
    render stream: true # Streaming enabled
  end
end

According to the documentation:

Streaming may be considered to be overkill for lightweight actions like new or edit. The real benefit of streaming is on expensive actions that, for example, do a lot of queries on the database.

So, if you're not using streaming, is there still a difference?

Yes.

The difference is a template can define multiple content blocks by calling content_for multiple times. Doing so will concatenate the blocks and pass that to the layout:

# layout.html.erb
<div class="heading"><%= yield :surprise %></div>
<div class="body">
   <p><%= yield %></p>
   <p>But it's not very interesting...</p>
</div>

# template.html.erb
<%= content_for :surprise, "Hello" %>
I've got your content!
<%= content_for :surprise, ", World!" %>

# Generated HTML
<div class="heading">Hello, World!</div>
<div class="body">
   <p>I've got your content!</p>
   <p>But it's not very interesting...</p>
</div>

Since provide doesn't continue searching the provided template, only the block passed to the first provide call will be sent to the template:

# layout.html.erb
<div class="heading"><%= yield :title %></div>

# template.html.erb
<%= provide :title, "Foo" %>
<%= provide :title, "bar" %>

# Generated HTML
<div class="heading">Foo</div>
Maje answered 24/9, 2015 at 16:13 Comment(1)
provide also append contents in view_flow. only way to overwrite contents for key is using content_for with option flush: true.Ernest
P
7

Was curious to see what the difference was, and as Thong Kuah pointed to the api, inside the answer:

This means that, if you have yield :title in your layout and you want to use streaming, you would have to render the whole template (and eventually trigger all queries) before streaming the title and all assets, which kills the purpose of streaming. For this reason Rails 3.1 introduces a new helper called provide that does the same as content_for but tells the layout to stop searching for other entries and continue rendering.

Pleasing answered 5/5, 2015 at 10:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.