Rails 3: yield/content_for with some default value?

Ruby on-RailsRubyYield

Ruby on-Rails Problem Overview


Is there any way to detect if #content_for was actually applied to a yield scope in Rails?

A classic example being something like:

<title><%= yield :page_title %></title>

If a template doesn't set that with

<% content_for :page_title, "Something here" %>

Is there a way to have the layout put something else there instead?

I tried defining it with #content_for in the layout itself, but this just causes the text to be doubled-up. I also tried:

<%= (yield :page_title) or default_page_title %>

Where #default_page_title is a view helper.

This just left the block completely empty.

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

You can use content_for? to check if there is content with a specific name:

<% if content_for?(:page_title) %>
  <%= yield(:page_title) %>
<% else %>
  <%= default_page_title %>
<% end %>

or

<%= content_for?(:page_title) ? yield(:page_title) : default_page_title %>

Then in your views you can specify the content like

<% content_for :page_title do %>
    Awesome page
<% end %>

Solution 2 - Ruby on-Rails

As of Rails 3, yield() returns empty string if there was no content for the requested key, so you can do something like this:

<title><%= yield(:page_title).presence || 'Default Page Title' %></title>

In your application helper, if you define:

def page_title(title = nil)
  title ? content_for(:page_title) { title } : content_for(:page_title).presence
end

Then you can do something like this:

<title><%= page_title or 'Default Page Title' %></title>

And in your views you can customize with:

<% page_title 'My Page Title' %>

Solution 3 - Ruby on-Rails

Better answer for rails 3 here:

https://stackoverflow.com/questions/2907245/yield-and-default-case-do-not-output-default-case

<%= yield(:title).presence || 'My Default Title' %>

Solution 4 - Ruby on-Rails

I have just released a new gem, dry_views, which provides enhanced content_for methods.

The benefit over the accepted answer is it allows you to remove all logic (if/else) from your views.

See the readme for extensive examples:

- no_content_for :key

  • Use a dash (-) not equals (=)
  • Will prevent a later content_for from rendering. This allows template overrides of layouts.

= content_for_with_default :key

  • Use an equal (=) not a dash (-)
  • You provide it with the default HAML via a block or a set of params that are the same as you would pass to a standard "render" call (i.e. :partial => 'foo', :locals => {:bar => 'bar'}).`

= content_for :key

  • Use an equal (=) not a dash (-)
  • You provide it with a block, i.e. {render :partial => 'foo'}, and it will override content_for_with_default. It has the same precedence as no_content_for, so whichever is rendered first wins, so if a layout has either no_content_for or content_for (with or without default) the template can now override it.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questiond11wtqView Question on Stackoverflow
Solution 1 - Ruby on-RailsDylan MarkowView Answer on Stackoverflow
Solution 2 - Ruby on-RailsJoshua CoadyView Answer on Stackoverflow
Solution 3 - Ruby on-RailscyrilchampierView Answer on Stackoverflow
Solution 4 - Ruby on-RailsPeter H. BolingView Answer on Stackoverflow