Skip to content
Permalink
Browse files
Replace pagination with turbo (#80)
* fetch upstream

* Change: Update toggle controller to have option perform toggle on load

* Change: Add turbo frames to developer index in order to leverage turbo for infinite scroll
Change: Pagy helper to use updated toggle controller for hiding when detecting javascript
Remove: Unneccessary pagination controller due to leveraging turbo
Remove: Developer index.turbo_stream

* Remove: @rails/request.js from bundle as we are using turbo for pagination

* Run `yarn install`

* Center pagination (shown when JS is disabled)

* Change: Match desired syntax style for index.html
Change: Make toggle controller more powerful and allow setting of open state
Change: Update header to use new syntax
Change: Update header to > Stimulus 2.0 syntax for defining targets
Change: Update pagy links to use newer toggler syntax

* Change: Ignore first page developers in turbo stream since it is already rendered outside of frame

* Change: Remove extra blank line

Co-authored-by: Joe Masilotti <joe@masilotti.com>
  • Loading branch information
Tonksthebear and joemasilotti committed Nov 11, 2021
1 parent a130c48 commit 6c9f3dd
Show file tree
Hide file tree
Showing 11 changed files with 43 additions and 70 deletions.
@@ -30,3 +30,8 @@
!/app/assets/builds/.keep

/node_modules

# Ignore local docker config
.dockerdev/*
dip.yml
docker-compose.yml
@@ -1,5 +1,5 @@
.pagy-nav {
@apply flex space-x-2;
@apply flex space-x-2 mb-8 justify-center;
}

.pagy-nav .page a,
@@ -2,6 +2,6 @@ module PagyHelper
include Pagy::Frontend

def pagy_links(**args, &block)
tag.div(id: "pagy-links", **args, data: {pagination_target: "links"}, &block)
tag.div(id: "pagy-links", **args, data: {controller: "toggle", toggle_close_class: "invisible", toggle_target: "element"}, &block)
end
end
@@ -6,8 +6,5 @@ import { application } from "./application"
import FileUploadController from "./file_upload_controller.js"
application.register("file-upload", FileUploadController)

import PaginationController from "./pagination_controller.js"
application.register("pagination", PaginationController)

import ToggleController from "./toggle_controller.js"
application.register("toggle", ToggleController)

This file was deleted.

@@ -2,13 +2,31 @@ import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
static targets = ["element"]
static classes = ["visibility"]
static classes = ["open", "close"]
static values = {
open: { type: Boolean, default: false }
}

openValueChanged() {
this.openValue ? this.toggleOpen() : this.toggleClose()
}

toggle(event) {
event.preventDefault()
event && event.preventDefault()
this.openValue = !this.openValue
}

toggleOpen() {
this.elementTargets.forEach(element => {
this.hasCloseClass && element.classList.remove(this.closeClasses)
this.hasOpenClass && element.classList.add(this.openClasses)
})
}

toggleClose() {
this.elementTargets.forEach(element => {
element.classList.toggle(this.visibilityClass)
this.hasOpenClass && element.classList.remove(this.openClasses)
this.hasCloseClass && element.classList.add(this.closeClasses)
})
}
}
@@ -12,14 +12,24 @@
</p>
</div>

<div data-controller="pagination" data-pagination-visibility-class="invisible" class="mt-16 mx-auto sm:mt-24">
<ul id="developers" data-pagination-target="entries" role="list" class="space-y-8 bg-gray-100">
<div class="mt-16 mx-auto sm:mt-24">
<ul id="developers" role="list" class="space-y-8 bg-gray-100">
<%= render @developers %>
</ul>

<div class="bg-gray-100 pt-8">
<div class="mt-8">
<%= pagy_links do %>
<%== pagy_nav @pagy %>
<% end %>
</div>

<%= turbo_frame_tag "developers-#{@pagy.page}" do %>
<%= turbo_stream.append "developers" do %>
<%= render @developers unless @pagy.page == 1%>
<% if @pagy.next %>
<%= turbo_frame_tag "developers-#{@pagy.page + 1}", src: [:developers, page: @pagy.next], loading: :lazy %>
<% end %>
<% end %>
<%= turbo_stream.remove "developers-#{@pagy.page}" %>
<% end %>
</div>

This comment has been minimized.

Copy link
@rockwellll

rockwellll Nov 29, 2021

Contributor

@Tonksthebear this is a wonderful PR. Could you explain this bit. I'm having a hard time wrapping my head around this. Will it perform a loop untill every page has been rendered in a lazy loaded frame?. Thanks

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

This comment has been minimized.

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 👍

This file was deleted.

@@ -1,4 +1,4 @@
<div data-controller="toggle" data-toggle-visibility-class="hidden" class="bg-gray-800">
<div data-controller="toggle" data-toggle-close-class="hidden" class="bg-gray-800">
<div class="relative pt-6 pb-8">
<div>
<div class="max-w-7xl mx-auto px-4 sm:px-6">
@@ -42,7 +42,7 @@
</div>
</div>

<div data-target="toggle.element" class="hidden absolute z-10 top-0 inset-x-0 p-2 transition transform origin-top-right md:hidden">
<div data-toggle-target="element" class="hidden absolute z-10 top-0 inset-x-0 p-2 transition transform origin-top-right md:hidden">
<div class="rounded-lg shadow-md bg-white ring-1 ring-black ring-opacity-5 overflow-hidden">
<div class="px-5 pt-4 flex items-center justify-between">
<div>
@@ -5,7 +5,6 @@
"@hotwired/stimulus": "^3.0.1",
"@hotwired/turbo-rails": "^7.0.1",
"@rails/activestorage": "^6.1.4-1",
"@rails/request.js": "^0.0.5",
"@tailwindcss/aspect-ratio": "^0.3.0",
"@tailwindcss/forms": "^0.3.4",
"autoprefixer": "^10.3.7",
@@ -74,11 +74,6 @@
dependencies:
spark-md5 "^3.0.0"

"@rails/request.js@^0.0.5":
version "0.0.5"
resolved "https://registry.yarnpkg.com/@rails/request.js/-/request.js-0.0.5.tgz#5ea896085f1c82bacca0e32aa918c8cd003a3d30"
integrity sha512-EDw3d5oCSPFn/os7C41+61urT5J5n+bMXK2NTQVhJI8VCPAIVbvFGjZIQI0a9zJ9KuaWKHM6IjXTRakKemlzoA==

"@tailwindcss/aspect-ratio@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@tailwindcss/aspect-ratio/-/aspect-ratio-0.3.0.tgz#f779ab3f07cac848fb844122d6662ba6560010b8"

0 comments on commit 6c9f3dd

Please sign in to comment.