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?

4
  • 3
    What is your first intention using helpers? Why not do this in template instead? Commented Aug 24, 2010 at 22:24
  • Hmmmm, I guess I could use a template, I'm not sure why I didn't think of that. Commented Aug 24, 2010 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 ;-) Commented Aug 25, 2010 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. Commented Oct 11, 2010 at 9:28

3 Answers 3

47

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Yeah I agree that if I were to do it this way, this is the best way to do it. But as the other guys said, I"m going to use a partial instead. Thanks!
don't you need a concat for the inner content_tag?
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.

1 Comment

Worked like a charm for me, and is nice and simple when I only need to generate a tag or two from the helper.
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.