how can I add pagination of child widgets ?

34 views
Skip to first unread message

John Lane

unread,
May 1, 2014, 2:49:02 PM5/1/14
to cells-an...@googlegroups.com
Hello, I have an "Items"widget that contains "Item" widgets. Currently, as a test, it just pulls all items and it works.

  # app/widgets/items_widget.rb
  class ItemsWidget < ApplicationWidget

    def display
      Item.all.each do |i|
        self << widget('items/item', i.id, item: i)
      end
      render
    end

  end

and

  <!-- app/widgets/items/display.html.erb -->
  <%= widget_div do %>                                                                         
  <% for item in children do %>
    <%= render_widget item %>
  <% end %>
<% end %>

They're invoked from a controller action:

  <!--app/views/items/index.html.erb-->
  <%= render_widget :items %>

So the display state creates widgets for all items and then renders which renders each of those widgets.

I want to add pagination (with will_paginate or kaminari) but I can find little up-to-date information, so I'd like to know how I should add pagination to the above. I've only seen a couple of things by Nick that date back to 2010 but say they're deprecated. Basically I want the "display" of the "items" widget to paginate its render_widget calls for its item widgets.

Here is what I have tried (with will_paginate)

  (a) changing the above to "Item.all.paginate.each" and "<%= render_widget item %>"
  (b) changing the display view to "<%= will_paginate render_widget(:item) %>"

However that doesn't work. Any pointers appreciated... many thanks.

John

Nick Sutterer

unread,
May 1, 2014, 6:08:54 PM5/1/14
to cells-an...@googlegroups.com
Hi John,

I don't really understand what doesn't work - are the paginate links incorrectly displayed or what?


Nick


--
Sie erhalten diese Nachricht, weil Sie in Google Groups E-Mails von der Gruppe "Cells and Apotomo" abonniert haben.
Wenn Sie sich von dieser Gruppe abmelden und keine E-Mails mehr von dieser Gruppe erhalten möchten, senden Sie eine E-Mail an cells-and-apot...@googlegroups.com.
Weitere Optionen finden Sie unter https://groups.google.com/d/optout.

John Lane

unread,
May 2, 2014, 5:38:46 AM5/2/14
to cells-an...@googlegroups.com

Hi Nick,

Ok, so I guess from what you ask, I am not completely off track. That is good.

What I have is an "items" widget that renders multiple "item" widgets using the code that I previously posted. To test it, I have a controller action that has this one-line view

  <%= render_widget :items %>

When I change the item query from "item.all.each" to "item.all.paginate.each", the controller action results in an error:

  Started GET "/" for 127.0.0.1 at 2014-05-02 10:34:13 +0100
  Processing by ItemsController#index as HTML
  Rendered items/index.html.erb within layouts/application (1.9ms)
  Completed 500 Internal Server Error in 4ms

  ActionView::Template::Error (wrong number of arguments (0 for 1)):
    1: <%= render_widget :items %>
  app/widgets/items_widget.rb:4:in `display'
  app/views/items/index.html.erb:1:in `_app_views_items_index_html_erb___2290554104468074472_69935533011680'

Here is the offending app/widgets/items_widget.rb in its entirety. If I remove the word "paginate" from line 4, everything works (albeit without pagination).

     1    class ItemsWidget < ApplicationWidget
     2   
     3      def display
     4        Item.all.paginate.each do |i|
     5          self << widget('items/item', i.id, item: i)
     6        end
     7        render
     8      end
     9   
    10    end

Nick Sutterer

unread,
May 2, 2014, 6:24:16 AM5/2/14
to cells-an...@googlegroups.com
That somehow rings a bell - we had that before. I guess it was something in combination with will_paginates magic and Rails helpers. If you send me a small example project I will have a look into it!

Thx

John Lane

unread,
May 2, 2014, 12:54:09 PM5/2/14
to cells-an...@googlegroups.com
Hey Nick,

I have put together a test app and some notes. See https://github.com/johnlane/apotomo-pagination and its "README.md" that explains everything I did to make the test app.

To see it working (without pagination changes):

    git checkout a2b97a13

To reinstate pagination (which does not work)

    git checkout master

I haven't added paging controls to the view - no point until it can display the first page correctly.

ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-linux] Rails 4.0.1

Thanks,
John

Nick Sutterer

unread,
May 2, 2014, 6:55:15 PM5/2/14
to cells-an...@googlegroups.com
John - you need to call #paginate with at least one argument (options) per method signature.

Item.all.paginate(page: params[:page])

Nick

John Lane

unread,
May 3, 2014, 5:16:51 AM5/3/14
to cells-an...@googlegroups.com
Ah yes, true enough. And I read the method signature before coding. I looked but didn't see. Too often easily done!

I changed the code and moved on. In app/widgets/items_widget.rb I now have

      def display
        Item.all.paginate(page: params[:page], per_page: 10).each do |i|
          self << widget('items/item', i, item: i)
        end
        render
      end

 I get another error triggered in the view:

  undefined method `total_pages' for #<ActiveSupport::SafeBuffer:0x007f644825b378>

Having done some digging I discovered that, if I don't use "will_paginate" in  the view then it works. I can request a specific page with "http://localhost:3000?page=n" and it does what I expect.

What's going on is the "will_paginate" helper expects a "paginated collection" and "render_widget item" doesn't return a paginated collection. The job of the "will_paginate" helper is to generate the HTML for the pagination links. I am going to look into doing that manually but wondered if this is the right track or if there is something else that I should be doing.

Thanks,
John

Nick Sutterer

unread,
May 3, 2014, 5:40:35 AM5/3/14
to cells-an...@googlegroups.com
Try to include the WillPaginate helper in your widget class with
helper WillPaginate::Helper (or whatever)

Sorry for that invonvenience, but helpers in Rails are a mess. You could provide the total_pages method yourself in the widget. I might play around with your code some more.

John Lane

unread,
May 3, 2014, 7:55:59 AM5/3/14
to cells-an...@googlegroups.com
Well I decided to roll it myself and I would have ended up needing to do that anyway because I need to format the pagination HTML anyways (I'm using Bootstrap).

What I did was to write a "paginate" helper in the "app/widgets/items_widget.rb" that takes in a current page number and the number of pages. Those I pass to the view as locals in the render done in "app/widgets/items_widget.rb". The view then calls the paginate helper, which generates the pagination HTML that's compatible with the bootstrap CSS I'm using. See below diff:


git diff 305afd5623d 1c892512650daf
diff --git a/app/widgets/items/display.html.erb b/app/widgets/items/display.html.erb
index 7f60a5b..50307cd 100644
--- a/app/widgets/items/display.html.erb
+++ b/app/widgets/items/display.html.erb
@@ -2,7 +2,4 @@

   <% for item in children do %>
     <%= render_widget item %>
   <% end %>
-
-  <%= paginate page: page, pages: pages %>
-
 <% end %>
diff --git a/app/widgets/items_widget.rb b/app/widgets/items_widget.rb
index beb2671..3d02c98 100644
--- a/app/widgets/items_widget.rb
+++ b/app/widgets/items_widget.rb
@@ -1,33 +1,10 @@
 class ItemsWidget < Apotomo::Widget
 
   def display
-    items = Item.all.paginate(page: params[:page], per_page: 10)
-    items.each do |i|
+    Item.all.paginate(page: params[:page], per_page: 10).each do |i|

       self << widget('items/item', i, item: i)
     end
-    render locals: { page: params[:page], pages: items.total_pages }
+    render
   end
 
-  private
-  def paginate(args)
-    page = args[:page] ? args[:page].to_i : 1
-    pages = args[:pages] ? args[:pages].to_i : 1
-
-    first_css = %{class="disabled"} if page == 1
-    last_css  = %{class="disabled"} if page == pages
-
-    links = %{<ul class="pagination">\n}
-    links << %{  <li #{first_css}><a href="#{url_for}">&laquo;</a></li>\n}
-    1.upto pages do |p|
-      current_css = %{class="active"} if p == page
-      links << %{  <li #{current_css}><a href="#{url_for(params.merge({page: p}))}">#{p}</a><
-    end
-    links << %{  <li #{last_css}><a href="#{url_for}?page=#{pages}">&raquo;</a></li>\n}
-    links << %{</ul>\n}
-
-    links.html_safe
-
-  end
-  helper_method :paginate
-
 end

I've pushed my changes to my git repo if you want to look.
Reply all
Reply to author
Forward
0 new messages