Table: RailsNotes
User: dreamable
Created at: 2021-02-06 00:59:29 UTC
Updated at: 2021-02-06 01:02:09 UTC
Reference:(Table ID 3, Record ID 21)

标题 :
Rails Load More
笔记 :

I want to show user activity history which could be huge in AJAX way.

Basically I follow this guide, with improvement from this guide

  • views/home/activities.html.erb
<div class="container">
  <%= render partial: "home/activity", collection: @activities %>
</div>
<div class="load-more-container">
  <%= image_tag "ajax-loader.gif", style: "display:none;", class: "loading-gif" %>
  <%= link_to "Load More", "#", class: "load-more"%>
</div>

Please note
1. the partial is under views/home/ instead of views/activities by default. So we need to specify the path, instead of using <%= render @activities%> directly.
2. don't add remote: true in the link_to tag, otherwise, it will fetch twice, one with id, the other one without, leading to duplicate result.
3. download the ajax-loader.gif to assets/images

  • model/activity.rb
    default_scope { order('id DESC') }

I use ID instead of created_at because we do not have index for created_id. Usually, the ID order should be same with created_at, with some corner case exceptions.

  • views/home/_activity.html.erb
<div class="record" data-id="<%= activity.id %>">
  <%= activity.id %> | <%= activity.operation %> | <%= activity.table.name %>  | <%= activity.record_id %> | <%= activity.updated_at %>
</div>

Please not that you must specify the class and data-id attribute, which are use by the Javascript below.

  • javascript/packs/utils.js
$(document).on("turbolinks:load", function() {
  // when the load more link is clicked
  $('a.load-more').click(function (e) {
    console.log("Load-more clicked")
    // prevent the default click action
    e.preventDefault();
    // hide load more link
    $('.load-more').hide();
    // show loading gif
    $('.loading-gif').show();
    // get the last id and save it in a variable 'last-id'
    var last_id = $('.record').last().attr('data-id');
    console.log(`Last ID: ${last_id}`)
    // make an ajax call passing along our last user id
    $.ajax({
      // make a get request to the server
      type: "GET",
      // get the url from the href attribute of our link
      url: $(this).attr('href'),
      // send the last id to our rails app
      data: {
        id: last_id
      },
      // the response will be a script
      dataType: "script",
      // upon success
      success: function () {
        // hide the loading gif
        $('.loading-gif').hide();
        // show our load more link
        $('.load-more').show();
      }
    });
  });
});
  • controllers/home_controller.rb
  def activities
    if params[:id]
      @activities = Activity.where(user: current_user, id: (0..params[:id].to_i-1)).limit(5)
    else
      @activities = Activity.where(user: current_user).limit(5)
    end
    respond_to do |format|
     format.html
     format.js
    end
  end

Please note that the class variable solution here does not work, reloading the page will not reset the class variable, leading to empty page.

  • views/home/activities.js.erb
<% if @activities.empty? %>
  $('.load-more-container').hide()
<% else %>
  $('.container').append('<%= escape_javascript(render(partial: "home/activity",collection: @activities)) %>')
<% end %>

Please note that we use the solution here to disable the link once all loaded. It depends on the class container and load-more-container set in views/home/activities.html.erb

Tag: