8

In a Rails 3.2 index view I am rendering two partials.

<%= render :partial => 'users/user', :locals => {:users => @happy_users} %>
<%= render :partial => 'users/user', :locals => {:users => @sad_users} %>

and in the partial

<% users.each do |user| %>
  Show some fields
<% end %>
<%= will_paginate users %>

The pagination is not working.

If I alter will_paginate to take an instance variable, pagination works (but the wrong collection)

<%= will_paginate @users %>

How can I pass locals to will_paginate when the partial is called?

(I realize that I'll also need to pass :param_name to get this working with two collections. For now I'm just trying to get one instance working.)

The partial is rendered via index.js.erb

$(".ajaxable-users").html('<%= escape_javascript(render("users/user")) %>');

And the controller looks like

def index
  @users = User.scoped.paginate(:page => params[:page], :per_page => 5)
  @happy_users = User.happy_scope.paginate(:page => params[:page], :per_page => 5)  
  @sad_users = User.happy_scope.paginate(:page => params[:page], :per_page => 5)  

  respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @users }
      format.json { render :json => @users }
      format.js
  end
end

Thanks for any ideas.

Viktor Trón
  • 8,774
  • 4
  • 45
  • 48
Andy Harvey
  • 12,333
  • 17
  • 93
  • 185
  • when you say that the pagination isn't working, what happens? – Frederick Cheung May 25 '12 at 07:20
  • nothing! I have a javascript function making the ajax request. The function runs (it adds a spinner.gif) but nothing else happens. Do you need a log? This issue seems strange to me. Everything works fine with `@user` but falls apart when I try to pass as a local. Is there any reason why will_paginate would not accept a local? – Andy Harvey May 25 '12 at 07:51
  • Why aren't you passing any locals when you do escape_javascript(render("users/user")) ? – Frederick Cheung May 25 '12 at 08:19
  • Thanks, I tried that as per gmile's answer below. But perhaps I'm not understanding fully how to implement this. To pass locals in `escape_javascript(render("users/user"))` would require me having two index.html.erb files, correct? One for each collection that I need to render? – Andy Harvey May 25 '12 at 08:43
  • Not necessarily, but clearly when you make that call to render you have to say which collection you want rendered – Frederick Cheung May 25 '12 at 09:37
  • Just hint here: You would able to get different params on different situation right? – chaitanya May 28 '12 at 13:04

3 Answers3

4
  1. you need to make sure the controller knows which page link of which listing you press. we do this by passing a param to will_paginate page link generator.
  2. you need to make sure your js updates the correct html tag when refreshing the pagination of either listing. we do this by wrapping the pagination in a mood-dependent :) div tag.
  3. since we share the partial we need to make sure mood as well as correct collection is set in controller and passed to partial as local from index views (both html and js).
  4. finally make sure your ajax remote picks up after you redraw pagination (this is a bonus)

so, to set controller instance vars depending on request mood parameter

  • note that this code also spares you some db calls, since if you just pagintae sad users you do not need to retrieve happy ones or all).
  • I ommit @users for simplicity, its never used
  • I suspect happy scope for sad users is only a typo

.

# controller
def index
  @mood = params[:mood]
  @mood_users = @happy_users = User.happy_scope.paginate(:page => params[:page], :per_page => 5) if @mood.blank? || @mood == 'happy'
  @mood_users = @sad_users = User.happy_scope.paginate(:page => params[:page], :per_page => 5) if @mood.blank? || @mood == 'sad'

  respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @users }
      format.json { render :json => @users }
      format.js
  end
end

make sure we dispatch the right locals to partials:

# index.html.erb
<%= render :partial => 'users/user', :locals => {:users => @happy_users, :mood => 'happy' } %>
<%= render :partial => 'users/user', :locals => {:users => @sad_users, :mood => 'sad' } %>

make partial mood sensitive allowing for distinct div tag id. also put mood in request url.

# users/user partial
<div id='<%= "#{mood || ''}users"%>'>
<% users.each do |user| %>
  Show some fields
<% end %>
<%= will_paginate users, :params => { :mood => mood } %>
</div>

this js allows for refreshing different collections in different divs depending on which pagination link was pressed.

# index.js.erb
$("#<%= @mood %>users").html('
  <%= escape_javascript(render(partial: "users/user", locals: { users: @mood_users, mood: @mood })) %>
');

For unobtrusive ajax links for pagination you need. Ruby - Rails 3 - AJAXinate Paginate and sorting

# in a generic js
$(document).ready(function () {
    $(".pagination").find("a").livequery(function () {
        $(this).attr("data-remote", true);
    });
});

Note that the solution should even work if javascript is not enabled and we fall back on html. In this case both sections should be redisplayed allowing for different pages.

MODIFIED CODE TO ALSO HANDLE GRACEFUL JS FALLBACK TO HTML

controller

# controller
def index
  @mood = params[:mood]
  @happy_page = @mood == 'happy' ? params[:page] : params[:happy_page]
  @sad_page = @mood == 'sad' ? params[:page] : params[:sad_page]

  @mood_users = @happy_users = User.happy_scope.paginate(:page => @happy_page, :per_page => 5) if !request.xhr? || @mood == 'happy'
  @mood_users = @sad_users = User.happy_scope.paginate(:page => @sad_page, :per_page => 5) if !request.xhr? || @mood == 'sad'
  @mood_users = @users = User.scoped.paginate(:page => params[:page], :per_page => 5) if @mood.blank?

  respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @users }
      format.json { render :json => @users }
      format.js
  end
end

forward new view vars to partial local

# index.html.erb
<%= render :partial => 'users/user', :locals => {:users => @happy_users, :mood => 'happy', :happy_page => @happy_page, :sad_page => @sad_page } %>
<%= render :partial => 'users/user', :locals => {:users => @sad_users, :mood => 'sad', :happy_page => @happy_page, :sad_page => @sad_page  } %>

we only need to pass this here to not get undefined method in partial.

# index.js.erb (fixed HTML syntax)
$("#<%= @mood %>users").html('
  <%= escape_javascript(render(partial: "users/user", locals: { users: @mood_users, mood: @mood, :happy_page => @happy_page, :sad_page => @sad_page })) %>
');

this will then persist happy pages in param if sad page link is clicked, and vica versa.

# users/user partial
<div id='<%= "#{mood || ''}users"%>'>
<% users.each do |user| %>
  Show some fields
<% end %>
<%= will_paginate users, :params => { :mood => mood, :happy_page => happy_page, :sad_page => sad_page  %>
</div>
Community
  • 1
  • 1
Viktor Trón
  • 8,774
  • 4
  • 45
  • 48
  • thanks viktor, this is a great answer. apologies for the slow response - i've been going through my issue carefully to discover any silly mistakes. unfortunately this is still not working for me. If I set this up without using locals everything works fine. As soon as I had locals, nothing works (javascript runs, but ajax request does not complete). looking in my the console I see `GET http://path/to/app/users 500 (Internal Server Error)` with locals, which is not there without locals. Any idea why adding locals would cause this? – Andy Harvey Jun 02 '12 at 07:19
  • just a quick question to check - can a collection/ array be set as a local? or does local expect strings/integers? – Andy Harvey Jun 02 '12 at 07:21
  • actually, scratch the `GET http://path/to/app/users 500 (Internal Server Error)`. That is unrelated. – Andy Harvey Jun 02 '12 at 07:32
  • dagnammit! This was a *really* frustrating issue. I modified the syntax of the index.js.erb function from `escape_javascript(render("users/user", locals:...` to `escape_javascript(render :partial => "users/user", locals:..`. The locals now work as expected. Frustrating! – Andy Harvey Jun 02 '12 at 10:01
  • even though my issue was something else, I'm going to mark your answer as the correct one. I think it's a very good guide for future searchers. For completeness, perhaps you'd like to add a small edit about the `render partial` issue I describe above? – Andy Harvey Jun 02 '12 at 10:04
  • @AndyHarvey edit done. so do you get this working? some other way? what was your issue? – Viktor Trón Jun 02 '12 at 13:09
  • using the syntax `render("partial` in index.js.erb, it is not possible to pass locals. modifying the syntax to `render partial: "partial"..` locals will work. I had run up against an issue where locals were not working in index.js.erb. Once I got passed that, your answer helped me quickly put the rest together. I used your solution, but with the modified index.js.html syntax. – Andy Harvey Jun 02 '12 at 14:39
  • i thought this quessie had a bounty already expiring yesterday, but now it says additional bounty, whaat does this all mean? :) – Viktor Trón Jun 02 '12 at 16:00
  • it means I don't know what I'm doing. Tried to award the bounty, but don't seem to be able to. Think I've initiated a new bounty in the process of trying to award. Any idea where I go to click to award the bounty after it has expired? – Andy Harvey Jun 02 '12 at 16:52
  • To award the bounty manually, click on the "+50" (or whatever bounty amount was allocated) button on the left side of the answer you want to award. http://meta.stackexchange.com/questions/16065/how-does-the-bounty-system-work – Viktor Trón Jun 02 '12 at 19:05
  • hi viktor, did you already receive the bounty for this question? – Andy Harvey Jun 06 '12 at 18:21
0

First, create in controller two diferent methods for each list for example fill_list1 and fill_list2 there are independent of method for create the main page for example index

Each list method must render a partial without layout for example

render :partial=>'fill_list1', :layout => false

The main page must call partials in view for first time.

when you click in paginate control you must hack click for call ajax next pagination items list

$('list1 a').live('click', function(event){
  event.preventDefault();
  $.ajax(
    {url:URLFORCALLMETHODLIST,
     success: function(content) {
                DIVLOCATIONLIST.html(content);
              },
     async:false,
    });
});
vicosanz
  • 57
  • 6
-1

Try passing proper locals in the index.html.erb:

$(".ajaxable-users").html('
  <%= escape_javascript(render("users/user", locals: { users: @users })) %>
');
oldhomemovie
  • 14,621
  • 13
  • 64
  • 99
  • thanks @gmile, unfortunately that hasn't improved things. Also I think that would make it difficult to achieve what I'm hoping to - i.e. to have two paginated and ajaxable lists on the same page, each displaying a different collection. – Andy Harvey May 15 '12 at 10:32