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:

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