Copy link
@Tonksthebear

Tonksthebear Nov 29, 2021

Author Contributor

Thanks @A7madXatab! You nailed it, each frame corresponds to a pagy page on the index. As you said, we lazy load so that the frame won't load the next page of contents until the user scrolls to that frame. I'll point out some extra considerations I made with this code.

First, I made use of turbo streams to manipulate the <ul> indirectly to avoid ugly html and turbo caching issues. You can achieve something similar to what I have without turbo streams by just leveraging turbo frames, but it leads to n+1 nested turbo frames (ugly in my opinion). It can also break browser back/forward buttons, which I'll get into later.

<%= render @developers unless @pagy.page == 1%> ensures we don't double render the first page. Basically, we want to be sure a user with javascript disabled can still browse properly. As such, when visiting the index for the first time, we first render the initial developers

  <ul id="developers" role="list" class="space-y-8 bg-gray-100">
     <%= render @developers %>
   </ul>

Then, inside our turbo frame, <%= render @developers unless @pagy.page == 1%> protects against re-rendering the first page of developers and appending it to the <ul>.

It sounds like you already understood this part:

      <% if @pagy.next %>
        <%= turbo_frame_tag "developers-#{@pagy.page + 1}", src: [:developers, page: @pagy.next], loading: :lazy %>
      <% end %>

It just adds the next frame to lazy load if there is another page of developers.

Now, since all of the above is nested inside a turbo_stream.append, the actual turbo frame that contained the turbo streams gets emptied after it renders. In case you don't know, after a <turbo-stream> element performs its function, it gets removed from the dom. This means we will have a bunch of empty <turbo-frame> tags. <%= turbo_stream.remove "developers-#{@pagy.page}" %> removes the parent turbo frame to prevent this. It also preserves the browser's back/forward buttons. I believe this is because the turbo frames get cached if they're still on the page and try to re-fetch their content when performing the native browser back/forward functions. Since we're removing frames when we're done with them, there won't be any <turbo-frame> elements to cache (besides any lingering next-page <turbo-frame> elements, which we want to keep anyways).

Let me know if you have any other questions. This was bigger than expected, but hopefully it's helpful 👍