views:

75

answers:

3

Hi,
how can I remember some state within a partial renderer?

Problem at hand: my partial _testitem.html.erb renders table rows of a collection of testitems. A Testitem has an id and a sortkey and the partial is called with

<%= render :partial => "testitem", :collection => @testbox.testitems.sort_by{|x| [x.sortkey, x.id]} %>

i.e. all testitems are sorted by the sortkey and then by their id. Now I'd like to display a table column that shows the current sortkey and I thought it would be neat to use

<td rowspan="<%= Testitem.count_by_sql "SELECT count(*) from testitems where sortkey=#{testitem.sortkey}" %>"><%=h testitem.sortkey %></td>

which of course breaks the table, as every <td> get's the rowspan. The expected result would look somewhat like

Group   |   Id  |    Description |
----------------------------------
   1    |    1  |    ...         |
        --------------------------
        |    2  |    ...         |
        --------------------------
        |    16 |    ...         |
----------------------------------
   2    |    3  |    ...         |
----------------------------------
   3    |    4  |    ...         |
        |   10  |    ...         |
        |   11  |    ...         |
 ---------------------------------

Is there a way to "remember" (within _testitem.html.erb) the fact that I added the <td rowspan=""> already for a given sortkey?

Thanks for helping me out.

A: 

One solution you could try... first, get a hash of all the rowspans necessary for each sortkey.

@rowspans = TestItem.count(:group => sortkey)

Then, in the partial, as you're rendering each row, set an instance variable to check if you're rendering rows with the current sortkey...

<% if testitem.sortkey != @current_sort_key %>
  <% @current_sort_key = testitem.sortkey %>
  <td rowspan="<%= @rowspans[@current_sort_key] -%>">
    <%= testitem.sortkey %>
  </td>
<% end %>
... render the rest of row ...

Depending on how clean you like your views to be, this might be better written inside of a helper, rather than in the view itself.

Cratchitimo
+1  A: 

In your model you could do something like:

class TestBox  
  named_scope :sorted_items, :sort_by => :sort_key, :id

  def grouped_items
    sorted_items.group_by(&:sort_key)
  end
end

and in your view you can do:

<% @test_box.grouped_items.each do |sort_key, items| %>
  <td rowspan="<% items.length %>"><%= render :partial => 'test_item', :collection => items %></td>
<% end %>

and in the test_item partial:

<%= h item.sort_key %>

I am not completely sure this will give you exactly what you are looking to do, but it will get you a good start in the right direction.

bobbywilson0
Sorry for getting back to you so late, but I had a view days off. I like the trick with the named_scope to do the sorting; the solution in the view doesn't work though, as the partial contains <tr>s and the <td rowspan> has to be inside the <tr>.
jhwist
+1  A: 

First, if you use all the testitems of a testbox anyway, you dont need that extra count queries.

What you want to do, is to draw the first item partial with the td of the group, and then draw the rest of the items regularly..

in the controller:

@testitems_by_keys = @testbox.testitems.group_by(&:sortkey)

in you view:

<% @testitems_by_keys.each do |group, items| %>
  <%= render :partial => "item", :locals => { :object => items.shift, :group => group, :size => items.size+1 } %>
  <%= render items %>
<% end %>

in _item:

<tr>
<% if local_assigns[:group].present?  %>
  <td rowspan="<%= size %>"><%= group %></td>
<% end %>
rest of the partial....
</tr>

note: just wrote it here.. may or may not contain syntax errors.

amikazmi
why make the @testitems_by_keys an instance variable? I think it makes more sense in the model since it is business logic. why are you adding 1 to items.size?
bobbywilson0
it's a gray area.. it's only a group by function.. not so much logic in it, but moving things to the model can never be a wrong choice :) add 1 to the items size because I take out the first item, so now the length is -1, but the rowspan need to be the items length
amikazmi
Well, the extra count-query was only to get the number of items for the current group. I finally went with your rendering solution and bobbywilson0's named_scope to do the sorting/grouping.
jhwist