content_for best practices

recently i ready about using content_for to add more dynamic control
in my layouts.

wanting to keep DRY, i'm wondering what the best way is to use this is
if i'm wanting to check specific areas for content. based on if there
is or isn't, i would like to totally remove or add elements to the
page.

while the example i am including works, it seems like this would
probably be creating a lot more work than necessary...

<% if(yield :sidebar) %>
  <div id="sidebar">
    <%= yield :sidebar %>
  </div>
<% end %>

if this isn't a good way of doing it, please let me know why. and if
there is an easy / clean way of doing it.

Thanks for the help

Josh wrote:

recently i ready about using content_for to add more dynamic control
in my layouts.

wanting to keep DRY, i'm wondering what the best way is to use this is
if i'm wanting to check specific areas for content. based on if there
is or isn't, i would like to totally remove or add elements to the
page.

while the example i am including works, it seems like this would
probably be creating a lot more work than necessary...

<% if(yield :sidebar) %>
  <div id="sidebar">
    <%= yield :sidebar %>
  </div>
<% end %>

if this isn't a good way of doing it, please let me know why. and if
there is an easy / clean way of doing it.

Maybe you can find some inspiration from Err:

http://errtheblog.com/post/28

Thanks for the reply Jacob. That blog post was actually one of the
first links I came across.

I liked the idea for conditionals like this example:

  <%= (sidebar = yield :sidebar) ? sidebar : render(:partial =>
'shared/sidebar') %>

I guess I could do something like so:

view.rhtml:
  <% content_for :sidebar do %>
    <div id="sidebar">
      all of my content in here
    </div>
<% end %>

and then in my layout:
  <%= (sidebar = yield :sidebar) ? sidebar : '' %>

but I'm still having to type that sidebar div everytime.

Josh wrote:

Thanks for the reply Jacob. That blog post was actually one of the
first links I came across.

I liked the idea for conditionals like this example:

  <%= (sidebar = yield :sidebar) ? sidebar : render(:partial =>
'shared/sidebar') %>

I guess I could do something like so:

view.rhtml:
  <% content_for :sidebar do %>
    <div id="sidebar">
      all of my content in here
    </div>
<% end %>

and then in my layout:
  <%= (sidebar = yield :sidebar) ? sidebar : '' %>

but I'm still having to type that sidebar div everytime.

If you have a default sidebar, then you could just put the div in the layout, right?

Or you could make it into a helper method, something like (untested):

def conditional_element(name)
   element = yield(name.to_sym)
   content_tag(:div, element, :id => name.to_s) if element
end

I'm not sure exactly what the problem you're trying to solve is, so maybe I'm misunderstanding something :slight_smile:

def conditional_element(name)
   element = yield(name.to_sym)
   content_tag(:div, element, :id => name.to_s) if element
end

I might could get something like that to work. Basically I am just
looking for a way to check and see if there is content for a specific
area and adjust my markup, ids and classes accordingly.

Say I have a 3 column layout, and if there is no sidebar content, i
want to be able to check and render a 2 column layout instead.

Josh wrote:

def conditional_element(name)
   element = yield(name.to_sym)
   content_tag(:div, element, :id => name.to_s) if element
end

I might could get something like that to work. Basically I am just
looking for a way to check and see if there is content for a specific
area and adjust my markup, ids and classes accordingly.

Say I have a 3 column layout, and if there is no sidebar content, i
want to be able to check and render a 2 column layout instead.

Sounds like you're trying to squeeze two templates into one with a resulting mess of conditional logic in the template. Perhaps it would be easier to just make two templates and then select the corresponding template from the controller dynamically? Or maybe I'm still not totally clear on what you're trying to achieve :wink:

Yes, that is what i was trying to do. haha...

Maybe in this case, i should just stick with simple instead of dry and
have two templates.

I did come up with something that worked for me for now though. This
is what I have currently:

<% left_col = yield :left_col -%>

  <% if left_col -%>
  <div id="left_col">
    <%= left_col -%>
  </div>
  <% end -%>

Thanks for your help though. I managed to learn a lot from the
examples you provided for other pieces of my project.

# helper

module ApplicationHelper

  def sidebar( args={}, html_options={}, &block )
    block_to_partial( 'shared/sidebar', args, html_options, block )
  end

  def block_to_partial( template, args, html_options, block )
    template_name = template.split('/').last
    args = { :title => args, :subtitle => nil } if args.is_a?(String)
&& !args.empty?
    html_options[:class] = if html_options[:class].nil?
      template_name
    else
      ( html_options[:class].split(' ') << template_name).join(' ')
    end
    vars = args.merge! :options => html_options,
                        :@content_for_layout => capture(&block)
    concat render(:partial => template,
                  :locals => vars), block.binding
  end

end

# shared/_sidebar.rhtml

<% content_tag 'div', options do -%>
<%= content_tag 'h3', title %>
<%= content_tag('h4', subtitle) unless subtitle.blank? %>
<%= yield %>
<% end %>

# view

<% sidebar 'Companies', :id => 'companies_sidebar' do %>
<ul>...html lives here...</ul>
<% end %>

# explanation

I use the nested_layouts plugin and I find it invaluable... This
helper pattern makes a perfect addition for when I need to pass things
to other layers of my layout. You can use this in your case where you
use content_for.

I found block_to_partial somewhere on the interweb (forgot the source;
if you want credit, speak up!). I customized it so that I can pass a
String as the first parameter, which automatically becomes the title
variable in the partial. Otherwise, I can pass a Hash for the vars
needed in the partial that I don't want to consider "content":

sidebar :title=>'Companies', :subtitle=>'Mortgage Brokers' {...}

rather than yielding these as the content, it lets me render them as
part of the partial, however I might want them commonly formatted in
my partial temlate (DRYness!).

Based on this pattern, I've defined other helpers that accomplish
similar tasks. I used title as the only convention for all partials so
I could pass a string for it. This might not be necessary for your
case, and if you need to define other things instead, you can just
pass a hash when you call it.

It works well for all my needs. Improvements? Suggestions?

--Andrew Vit