Rails what is the difference between content_for and yield? - ruby-on-rails

For example: content_for(:stuff) vs yield :stuff
I know they are implemented slightly differently, but is there any real functionality difference?
Is there a generally accepted best practice?

yield is how you specify where your content areas is going to go within a layout. You might have something like this:
<div>
<h1> This is the wrapper!</h1>
<%= yield :my_content %>
</div>
content_for is how you specify which content is going to be rendered into which content area. You might have something like this:
<% content_for :my_content do %>
This is the content.
<% end %>
The result would be
<div>
<h1> This is the wrapper!</h1>
This is the content.
</div>
They are opposite ends of the rendering process, with yield specifying where content goes, and content_for specifying what the actual content is.
Is there a generally accepted best practice?
The best practice is to use yield in your layouts, and content_for in your views. There is a special second use for content_for, where you give it no block and it returns the previously rendered content. This is primarily for use in helper methods where yield cannot work. Within your views, the best practice is to stick to yield :my_content to recall the content, and content_for :my_content do...end to render the content.

yield:
yield identifies a section where content from the view should be
inserted
content_for:
The content_for method allows you to insert content into a named yield
block in your layout. For example, this view would work with the
layout that you just saw:
yield :stuff will grab the contents that are pushed by content_for(:stuff)
So, using yield you can define the sections in your view/layouts and you use content_for for adding contents to those sections. Any unnamed yield will grab all other contents.
You can learn more about it reading the tutorial.

Calling #content_for stores a block of markup in an identifier for later use. In order to access this stored content in other templates, helper modules or the layout, you would pass the identifier as an argument to content_for.
yield can still be used to retrieve the stored content, but calling yield doesn't work in helper modules, while content_for does....more: http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-content_for

From the Ruby on Rails API:
Calling content_for stores a block of markup in an identifier for later use. In order to access this stored content in other templates, helper modules or the layout, you would pass the identifier as an argument to content_for.
Note: yield can still be used to retrieve the stored content, but calling yield doesn't work in helper modules, while content_for does.
You can then use content_for :not_authorized anywhere in your templates.
<%= content_for :not_authorized if current_user.nil? %>
This is equivalent to:
<%= yield :not_authorized if current_user.nil? %>
content_for, however, can also be used in helper modules.
https://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-content_for

Related

Ruby on Rails: provide vs content_for

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!
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>
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.

Difference between using provide() and assigning a variable for page titles in Rails?

Could someone explain why it is preferred when embedding ruby for things like page titles to use
<% provide(:title, 'Help') %>
and then using
<%= yield :title %>
rather than jus using a variable:
<% title = 'Help' %>
<%= title %>
I'm assuming its to do with the fact that you can yield before you have called provide() but if that is the case why is it not possible to call the variable before defining it?
Thanks :)
If you want to simply render a variable in the view, the second method will do.
However, provide and yield offer a various ways to build the rendered content. For, example, you pass instance variable like #posts which you may already assign a variable after a complicated algorithm, which you will not do in a view template.
http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html
http://guides.rubyonrails.org/layouts_and_rendering.html#understanding-yield
provide (or content_for) are used to pass some elements from the view to the layout, so if you have layout:
<html>
<head>
<title><%= yield :title %></title>
</head>
<body>
<%= yield %>
</body>
</html>
Than in all the views, you can set the content for title with provide or content for. Local variables cannot do this, as they only live in a given view.
provide stores a block of markup in an identifier for later use. In this case, 'Help' in the symbol :title. The provide is enclosed in <% %> to indicate it is executing this code and not printing out in the view.
yield in this case just spits that block back out. The yield is enclosed in <%= %> to indicate it is being printed out into the view.
Think of it as setting a variable and printing out a variable.
See: http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-provide for more information. Note that provide is really a wrapper for content_for so that's where the good stuff is in that link.

Loading a nav bar when in a specific controller rails

I am having a bit of trouble in rails.
What I want to do is to display some extra links in the application layout when a specific controller is in use. How do I do this?
I am loading the pages dynamically using jquery and I tried using <%if controller_name == "foo"%> then do some magic, without any success.
If somebody could point me in the right direction or even a jquery-rails rendering tutorial that would be great.
Thanks.
Check out the content_for magic provided by Rails. It allows you to specify something like this in your application layout:
<%= yield :header %>
And then in your individual templates do something like this:
<% content_for :header do %>
Content I want put in the header
<% end %>
Which basically results in the content inside the content_for block being captured and rendered at the point of the yield statement. So, you can specify that in the templates for your controller.

How can I use content_for to put something in :yield

I am in ruby 1.9.2, rails3.
So My website has some structures,
and I want to put menu in a middle of my webpage.
I am doing something like (within application.html.erb file)
blahblahblah
<div id="menu">
<%= yield :menu %>
<div>
blahblhablah
I have a file menu.html.erb which has menu structure for the site.
What can I do if I want to use a file within ./layout folder to be used to be part of that yield :menu? I was wondering, if I have to use content_for for every controller, and within every functions...
Btw, menu.html.erb will be different for each controller, so thats why I am yielding it.
In conclusion, I just want to include one common shared menu.html.erb pretty much everywhere.
You could do something like this in your views:
<% content_for(:menu) do %>
<%= render :partial => "/layouts/user_menu.html.erb" %>
<% end %>
You could try to combine this with controller.controller_name (not sure this works for Rails3) and load a different menu for each controller automatically.
You might consider watching the railscast on layouts, it's concise and helpful.
Numbers 7 and 8.
http://railscasts.com/episodes?search=layout

Dynamic Sidebar with Rails layout

For instance, i want to have my sidebar to have several dynamic content. Using other method will lead me to put query codes into View, which is not a good idea at all. I would like to keep any query in my Controller.
Currently as i know there are several ff. method:
Render a shared partial -> No where to put the query
render :partial => "shared/sidebar"
Content For -> Additional details in the comment
<%= yield :sidebar %>
<% content_for :sidebar do %>
Netscape<br>
Lycos<br>
Wal Mart<br>
<% end %>
3rd is write it directly to the layout file.
So how should I make this work?
IF you want this in every view, you can place the method that populates the necessary data in application_controller and use a before_filter to trigger it.
before_filter :load_sidebar
def load_sidebar
#data = Thingy.find(:all)
end
Then your partial or content_for element checks for #data and processes.
If you wanted to reduce the amount of code in your application_controller.rb, you may want to consider using the Cells gem.
This would allow you to define your 'query' in a separate cell controller, and you would render the content for it using something like render_cell :sidebar, :myquery inside your view.

Resources