Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails render collection in ul li

I have this model called Post, idaelly I would love to use just ONE partial + ONE layout to achieve the following.

when rendering a single object, outputs:

%div= post.body

and when rendering a collection, outputs:

%ul
  %li= post.body
  %li= post.body
  %li= post.body

Currently I have a partial posts/post.haml looking like: %li= post.body

and whenever I'm rendering a collection, I would do%ul=render @posts

The problems being:

  1. whenever rendering a collection, I have to put the rendering in %ul
  2. The partial is NOT usable for single object without %ul

Although in 90% of my use cases I'm rendering a collection of post, it doesn't make sense for a post partial not to be usable as a stand-alone template.


I thought I could do something like

# view
render partial: 'post', collection: @posts, layout: 'list_of_posts'

# list_of_posts
%ul= yield

# posts/post
%li= post.body

This would solve my first problem IF IT WORKS, but it doesn't. Apparently render_collection does not take the layout option, so it's practically a dead end from what I can find on rendering collection. (Spacer_template could potentially work, but

  1. </li><li> as a spacer is in no way a good piece of code..
  2. haml would not allow this)

As for my second problem, an easy way out would be rendering everything in divs, but I am really reluctant to do so when things should be in a list. But for the sake of making it work, it's probably the only clean-ish solution. div.list-of-posts > div.post instead of ul.posts > li


I know I can always do something like

# view - collection
%ul= render @posts, in_list: true

# view - object
= render @post

# posts/post.haml
- post_counter ||= false
- tag = post_counter ? :li : :div
= content_tag tag do
  = post.body

but in this case I still need to put a %ul whenever a collection is passed.

Or else I can do something similar but perhaps a tiny bit cleaner:

# view - collection of objects
= render 'posts', posts: @posts

# view - object
= render @post

# posts/posts.haml
%ul
  = render post

# posts/post.haml
- post_counter ||= false
- tag = post_counter ? :li : :div
= content_tag tag do
  = post.body

This one is the best/cleanest way I can come up with so far, any thoughts?

like image 784
Mu-An Chiou Avatar asked Nov 14 '12 16:11

Mu-An Chiou


2 Answers

Add another partial which renders the li tag and then invokes the regular partial:

  • app/views/posts/index.html.haml

    %ul.posts= render collection: @posts, partial: "posts/post_li"
    
  • app/views/posts/_post_li.html.haml

    %li= render post
    
  • app/views/posts/_post.html.haml

    = div_for post do
      ....
    
like image 78
meagar Avatar answered Nov 20 '22 11:11

meagar


You can have a single partial and inside the partial it can just check to see if the local passed to it is a collection or a single Post object.

# Render calls
render :partial => "posts/display_posts", :locals => {:posts => Post.first}
render :partial => "posts/display_posts", :locals => {:posts => Post.all} # collection

And your partial:

- if posts.is_a? Post # single item
  %div= posts.body
- else
  %ul
  - posts.each do |post|
    %li= post.body
like image 42
MrDanA Avatar answered Nov 20 '22 11:11

MrDanA