29

I'm trying my best to build a helper that outputs a <'ul> consisting of all the members of a collection. For each member of the collection I want to print out a <'li> that has a title, and a div of links to CRUD the member. This is pretty similar to what Rails outputs for scaffolding for the index view.

Here is the helper I've got:

def display_all(collection_sym)
  collection = collection_sym.to_s.capitalize.singularize.constantize.all

  name = collection_sym.to_s.downcase

  html = '' 

  html << "<ul class=\"#{name}-list\">"

  for member in collection do
    html << content_tag(:li, :id => member.title.gsub(' ', '-').downcase.strip) do
     concat content_tag(:h1, member.title, :class => "#{name}-title")
     concat link_to 'Edit', "/#{name}/#{member.id}/edit"
     concat "\|"
     concat link_to 'View', "/#{name}/#{member.id}"
     concat "\|"
     concat button_to 'Delete', "/#{name}/#{member.id}", :confirm => 'Are you sure?  This cannot be undone.', :method => :delete
    end
   end

   html << '</ul>'

 return html
end 

And that output exactly what I want. First of all, if anybody thinks there's a better way to do this, please feel free to correct me, I suspect that I'm doing this in a bass ackwards way, but at the moment its the only way I know how.

I then attempted to wrap the links in a div as follows:

def display_all(collection_sym)
  collection = collection_sym.to_s.capitalize.singularize.constantize.all

  name = collection_sym.to_s.downcase

  html = '' 

  html << "<ul class=\"#{name}-list\">"

  for member in collection do
     html << content_tag(:li, :id => member.title.gsub(' ', '-').downcase.strip) do
     concat content_tag(:h1, member.title, :class => "#{name}-title")
     concat content_tag(:div, :class => "links-bar") do
       concat link_to 'Edit', "/#{name}/#{member.id}/edit"
       concat "\|"
       concat link_to 'View', "/#{name}/#{member.id}"
       concat "\|"
       concat button_to 'Delete', "/#{name}/#{member.id}", :confirm => 'Are you sure?  This cannot be undone.', :method => :delete
     end
   end
 end

 html << '</ul>'

 return html
end 

However, I now no longer get any of the markup inside the div.links-bar output to the view. I'm sure this must have something to do with block and bindings, but I can for the life of me figure out what or how to go about fixing it. Can anybody offer any help?

SuperStormer
  • 4,997
  • 5
  • 25
  • 35
TheDelChop
  • 7,938
  • 4
  • 48
  • 70
  • 3
    What is your first intention using helpers? Why not do this in template instead? – Joshua Partogi Aug 24 '10 at 22:24
  • Hmmmm, I guess I could use a template, I'm not sure why I didn't think of that. – TheDelChop Aug 24 '10 at 22:57
  • 1
    partials is the way to go on this I would think...kudos for plowing through all that code...got a headache just reading it ;-) – Ryan Aug 25 '10 at 21:40
  • 2
    The basic problem isn't blocks or bindings, but that the string "html" you're creating is marked as non-HTML-safe. You could use the raw() function, though as others have said, partials or content_tag are much better ideas. Just thought I'd point out what the underlying problem is, for people who have similar-but-different issues later. – AlexC Oct 11 '10 at 09:28

3 Answers3

46

I agree with the comment above recommending the use of a partial... but if you DID need to do this in a helper, this is a cleaner way to implement:

def display_all(collection)
  content_tag(:ul, class: "list") do
    collection.collect do |member|
      concat(content_tag(:li, id: member.name.gsub(' ', '-').downcase.strip) do
        member.name
      end)
    end
  end
end

I'd pass in a collection explicitly rather than passing in a symbol to create a collection so you aren't always required to display ALL the records in a particular table at once. You could add pagination, etc.

Sheharyar
  • 73,588
  • 21
  • 168
  • 215
Dave Pirotte
  • 3,806
  • 21
  • 14
25

@Joe, You can still use your method display_all(collection_sym) Just use: return html.html_safe instead of: return html

I still find that in many situations, it is better to generate HTML from helpers, instead of using partials. So the html_safe function in Rails 3 will make sure that you generate HTML, instead of converting it to String.

AdrieanKhisbe
  • 3,899
  • 8
  • 37
  • 45
Linh Chau
  • 251
  • 3
  • 2
2

As @TheDelChop says, you need a concat for the inner content_tag, otherwise the output is just <ul></ul>

Here's what that looks like:

def display_all(collection)
  content_tag(:ul, :class => "list") do
    collection.collect do |member|
      concat(
        content_tag(:li, :id => member.name.gsub(' ', '-').downcase.strip) do
          member.name
        end
      )
    end
  end
end

More explanation here: Nesting content_tag in Rails 3

James Hibbard
  • 16,490
  • 14
  • 62
  • 74