Skip to content

Instantly share code, notes, and snippets.

@Merovex
Last active August 6, 2024 11:11
Show Gist options
  • Save Merovex/fe0602cc9db9b132bd7565e7d2055160 to your computer and use it in GitHub Desktop.
Save Merovex/fe0602cc9db9b132bd7565e7d2055160 to your computer and use it in GitHub Desktop.
Rails/Stimulus Infinite Scroll with Pagy circa Summer 2024

In the Controller:

@pagy, @activities = pagy(Activity.order(created_at: :desc).includes(:user, :trackable), limit: 4)
respond_to do |format|
  format.html
  format.json { render json: { entries: render_to_string(partial: "activities/activity", collection: @activities, formats: [ :html ]), pagination: view_context.pagy_nav(@pagy) } }
end

In the view:

<section data-controller="infinite-scroll" data-action="scroll@window->infinite-scroll#scroll">
  <div class="hidden" data-infinite-scroll-target="pagination">
    <%== pagy_nav(@pagy) %>
  </div>
  <div data-infinite-scroll-target="entries">
    <%= render partial: 'activities/activity', collection: @activities %>
  </div>
</section>

Stimulus Controller

import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="infinite-scroll"
export default class extends Controller {
  static targets = ["entries", "pagination"]
  connect() {
    this.lastUrl = "";
  }

  scroll() {
    let body = document.body,
      html = document.documentElement;

    let nextPage = this.paginationTarget.querySelector("[aria-label='Next']")

    if (nextPage == null || nextPage.href == "") {
      return
    }

    let height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight)
    if (window.innerHeight + window.scrollY >= height - 100) {
      this.loadMore(nextPage.href);
    }
  }
  async loadMore(url) {
    if (url === this.lastUrl) {
      return
    }

    this.lastUrl = url;
    try {
      const response = await fetch(url, {
        headers: {
          Accept: "application/json"
        }
      })
      if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)

      const data = await response.json()

      this.entriesTarget.insertAdjacentHTML("beforeend", data.entries)
      this.paginationTarget.innerHTML = data.pagination
    } catch (error) {
      console.error("Error loading more entries:", error)
    }
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment