0

I currently have endless paging setup like so:

events_controller.rb

class EventsController < ApplicationController

  respond_to :html, :js

  def index
    @events = Event.page(params[:page]).per(5)
    respond_with(@events)
  end

end

events/index.html.erb

<div id="events-container">
  <%= render :partial => 'events', :locals => {events: @events} %>
</div>

events/events.html.erb

<div id="events-table-container" data-events-url="<%= current_url %>">

  <table id="events-tbl">
    <tbody id="events-tbl-body">
      <%= render events %>
    </tbody>
  </table>

  <%= paginate events %>

</div>​

assets/javascripts/events/events_endless_paging.js

$(function() {
  var isScrolledIntoView;
  isScrolledIntoView = function(elem) {
    var docViewBottom, docViewTop, elemBottom, elemTop;
    docViewTop = $(window).scrollTop();
    docViewBottom = docViewTop + $(window).height();
    elemTop = $(elem).offset().top;
    elemBottom = elemTop + $(elem).height();

    return (elemTop >= docViewTop) && (elemTop <= docViewBottom);
  };

  if ($('#events-container .pagination').length) {
    $(window).scroll(function() {
      var url;
      url = $('#events-container .pagination .next a').attr('href');
      if (url && isScrolledIntoView('#events-container .pagination')) {
        $('#events-container .pagination').html("<span class='working-notice'>Fetching more...</span>")

        return $.getScript(url);
      }
    });

    return $(window).scroll();
  }
});

events/index.js.erb

$('#events-tbl-body').append('<%= j render(@events) %>');

<% if (@events.current_page < @events.num_pages) %>
    $('.pagination').replaceWith('<%= j paginate(@events) %>');
<% else %>
    $('.pagination').remove();
<% end %>

This works like a charm and all. This issue becomes when I try to integrate setTimeout ajax polling to refresh the events page.

assets/javascripts/events/event_poller.js

$(document).on('ready', function() {
  App.Pollers.Event.poll();
});

App.Pollers.Event = {

  frequency: 170000,

  poll: function() {
    setTimeout(App.Pollers.Event.request, App.Pollers.Event.frequency);
  },

  request: function() {
    eventsRequest = $.getScript($('#events-table-container').data('events-url'));
    return eventsRequest;
  },

};

Removing the endless paging code above, here's what the code in the events/index.js.erb would look like for just the refresh behavior to work properly:

events/index.js.erb

$('#events-container').html('<%= j(render :partial => 'events', :locals => {events: @events}, :formats => :html) %>');

App.Pollers.Event.poll();

My challenge is getting the endless paging code and the ajax refresh code working together. If I use the ajax refresh with the endless paging code, then what ends up happening is that duplicate events are appended to the events-tbl-body element. Another issue is let's say a user scrolls down the page and the endless paging appends page 2 results to page 1 results, then how does the ajax refresh code know how to display both pages 1 and 2? These are just a few of the challenges. Hoping that someone can provide guidance. I know this is a verbose question, so appreciate your attention.

keruilin
  • 16,782
  • 34
  • 108
  • 175

3 Answers3

0

I'm not entirely sure about the term 'endless paging', isn't there a limit to the amount of content, even though you don't want to show all content immediately?

This might work for you: https://github.com/paulirish/infinite-scroll

I would use this, for scrolling and getting more results, which is tried and tested:

bcm
  • 5,470
  • 10
  • 59
  • 92
0

I see there being two different uses for the poller, correct me if I'm wrong:

  1. Update existing, already on-page events with updates made to those events (including removal).
  2. Add in new events.

I would suggest, strongly, that you split your poller into two different pollers each responsible for a single actions.

The first one would take all the event ids for those events currently on the page and check for updates to them. Deleted events would be removed from the page; updated events would be updated in place.

The second one would look for events created after a 'last-time-you-polled' timestamp and insert those at the top of the page.

I'm not sure how the second poller would affect the pagination of your events for the endless page, but I think that challenge is solvable.

Carlos Drew
  • 1,633
  • 9
  • 17
0

My suggestion would be to create a shared AJAX method that checks how old the content is and then refreshes a particular piece of content (say an article, orderable item, etc.) and this piece of code is invoked either by the pagination code OR the automated timed poll.

Scenarios as examples

  1. User loads page(1/4) and it contains 4 pieces. The user doesn't scroll and you have 4 pieces to be updated at 170000 ms. Your updated timer would loop through those 4 items and call the independent AJAX updater for those 4.

  2. User loads page(1/4) and then scrolls down to page (2/4). Your pagination code renders out the new 4 pieces. The user keeps the page in place and so after 170000 ms you need to refresh those 4. Just like #1 above the timer call loops through items 5-8 (page 2's content items) and calls the independent updater for those 4.

  3. User loads page(1/4) and then scrolls down to page (4/4). Your pagination code renders out all the pieces (16 sections out of 16). The user keeps the page in place and so after 170000 ms you need to refresh those 4. Just like #1 & 2 above the timer call loops through items 20-24 and calls the independent updated for those 4.

  4. User loads page(1/4) and then scrolls down to page (2/4) and leaves it. Just like #2 above the 4 items are updated by the timer. The user then scrolls back up to page(1/4) and so the pagination/scroll code calls the independent function for (1-4) through a small (250ms) timer which checks to see if the content needs to be refreshed and does so.

  5. User loads page(1/4) and then scrolls down to page (4/4) and leaves it. Just like #4 the content refreshes on the last page and then they scroll up. As the scroll event occurs it triggers the ajax updater through a small (250ms) timer for the already existing items which checks to see if they need to be refreshed and if so does the update.

Considerations

  • It allows you to break the "does this need to update" logic into a shared function that accommodates the behavior of both the pagination and the user leaving the page still.
  • It allows you to ensure that you're only updating the content that is displayed and t his logic isn't duplicated in 2 sets of functions so your logic can be updated at a later date with minimal impact.
  • Having a small delay of this function when called by the pagination code when "scrolling" back up allows for the event where a user scrolls past an element so fast that it isn't worth doing the call and using the server resources to refresh content that isn't on the screen.
Community
  • 1
  • 1
Shawn E
  • 472
  • 2
  • 8