Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinite scroll pagination #192

Open
obsidienne opened this issue May 9, 2023 · 8 comments
Open

Infinite scroll pagination #192

obsidienne opened this issue May 9, 2023 · 8 comments

Comments

@obsidienne
Copy link
Contributor

Is your feature request related to a problem? Please describe.
To adapt the pagination to my use case, I need to create a custom component adding an infinite scroll behavior (something with Intersection Observer API probably)

Describe the solution you'd like
Create a custom component like cursor_pagination but based on intersection observer.

Describe alternatives you've considered
Do it myself

Additional context
Are you interested by a PR or have you already find a solution with the actual capacity ?

@woylie
Copy link
Owner

woylie commented May 10, 2023

I didn't implement this yet, but a PR is welcome! Should be based on LiveView streams now.

@obsidienne
Copy link
Contributor Author

obsidienne commented May 12, 2023

Hi, yes LiveView streams and the new phx-viewport-top & phx-viewport-bottom

https://youtu.be/FADQAnq0RpA?t=2373 and https://github.com/phoenixframework/phoenix_live_view/pull/2624/files

May be wait for it to ship?

@woylie
Copy link
Owner

woylie commented May 15, 2023

Yes, maybe better.

@Thr3x
Copy link

Thr3x commented Jul 3, 2024

Any update on this? It would be really nice

@woylie
Copy link
Owner

woylie commented Jul 3, 2024

LiveView has phx-viewport-top and phx-viewport-bottom attributes now. There's an example for infinite scroll in the documentation. All you need to do on the Flop side is to set the pagination type to :first or :last and call Flop.to_next_cursor/1 or Flop.to_previous_cursor/1 in your event handler.

@romarickb
Copy link

Hi @Thr3x how you handled it ?
Is it doable with Flop.Phoenix.table ?

@Thr3x
Copy link

Thr3x commented Nov 6, 2024

@romarickb Yeah I followed @woylie advice using cursor pagination in combination with an anchor.
phx-viewport-top and phx-viewport-bottom was really janky though so i switched to the anchor. It worked really well.

In my app.js i created a hook for the anchor:

  entries: {
    mounted() {
      observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            this.pushEvent("load-more", {});
          }
        });
      }, {
        root: null, // Use the viewport as the root
        rootMargin: '0px',
        threshold: 1.0 // Trigger when the element is fully visible
      });

      const anchor = document.getElementById('entries-anchor');
      if (anchor) {
        observer.observe(anchor);
      } else {
        console.error('Anchor element not found: #entries-anchor');
      }
    }
  },

And in the liveview I added the hook and the anchor below my table:

<div phx-hook="entries">
  <Flop.Phoenix.table>
    Your content here
  </Flop.Phoenix.table>
 <div id="entries-anchor"></div>
</div>

This then pushes the event to the server and can be handled below:

@impl true
  def handle_event("load-more", _, socket) do
    flop = Flop.to_next_cursor(socket.assigns.meta)

    case Clients.list_clients(flop) do
      {:ok, {clients, meta}} ->
        {:noreply,
         socket
         |> assign(:meta, meta)
         |> stream(:clients, clients)}

      _ ->
        {:noreply, socket}
    end
  end

Remember to modify your Flop Schema to use pagination. This was my setup. It only loads the first 25 entries, if the anchor is visible, it loads the next 25.

  @derive {
    Flop.Schema,
    filterable: [:id, , :first_name, :last_name, :birthday],
    sortable: [:id, :first_name, :last_name, :birthday],
    default_limit: 25,
    pagination_types: [:first, :last],
    default_pagination_type: :first,
    default_order: %{order_by: [:first_name, :id], order_directions: [:asc]},
  }

This should be everything to get you on the right track. I don't think I missed anything

@romarickb
Copy link

romarickb commented Nov 6, 2024

Awesome ! Thanks I was doing exactly the same kind of thing as you <div id="entries-anchor"></div>

I misunderstood Woylie advice as I thought it was possible to to it using phx-viewport-top and phx-viewport-bottom as the example provided.

Thank you for your very fast answer 😄

By the way your post is very clear and well explained !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants