Infinite Scroll with HOTWire – Part 2: Adding Stimulus

You can follow along if you missed the first part.

Infinite Scroll with HOTWire Part 1: Configuration
Ahmad khattab ・ Oct 17 ・ 3 min read

#rails
#ruby
#stimulus
#javasc…


This content originally appeared on DEV Community and was authored by Ahmad khattab

You can follow along if you missed the first part.

Adding Stimulus

Now as our data is ready and we can scroll to the bottom of the screen. We are ready to add a stimulus controller that is responsible for the pagination.

first, create a new file at app/javascript/controllers/pagination_controller.js

// pagination_controller.js


import { Controller } from "stimulus";

export default class extends Controller {
  static values = {
    url: String,
    page: Number,
  };

  initialize() {
    this.scroll = this.scroll.bind(this);
    this.pageValue = this.pageValue || 1;
  }

  connect() {
     document.addEventListener("scroll", this.scroll);
  }

  scroll() {
    if (this.scrollReachedEnd) {
      this._fetchNewPage()
    }
  }

  async _fetchNewPage() {
    // fetch new url
    // update new page
    // ensure that we are on the last page
  }

    get scrollReachedEnd() {
        const { scrollHeight, scrollTop, clientHeight } = document.documentElement;
        const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
        return distanceFromBottom < 20; // adjust the number 20 yourself
    }
  }
}

It's a simple controller that attaches a scroll listener on the element and calls _fetchNewPage when the scroll has reached the end. Now, let's populate the method body.

request.js is a minimalistic JavaScript pacakge that is set to replace Rails UJS in the near future. We will be using it to fetch new pages from the server. Let's install the package

yarn add @rails/request.js

Adding logic to the method body

What we want to do is that when the _fetchNewPage method is called, a) request the server the urlValue and add the pageValue as a query param.

  async _fetchNewPage() {
    const url = new URL(this.urlValue);
    url.searchParams.set('page', this.pageValue)

    await get(url.toString(), {
      responseKind: 'turbo-stream'
    });

    this.pageValue +=1;
  }

Let's connect the controller to the dom.

<div
  data-controller="pagination"
  data-pagination-url-value="<%= posts_url %> "
  data-pagination-page-value="<%= 2 %>">
  <%= render @posts %>
</div>

Adding tubro_stream responses

The requests made by the scroll is of type "text/vnd.turbo-stream.html". So, we'll need to handle that type of request.

create a new file named app/views/posts/index.turbo_stream.erb and add this code into it

<%= turbo_stream.append "posts" do %>
  <%= render @posts %>
<% end %>

with this, add id="posts" to the div for turbo to know where to append the new posts.

# posts/index.html.erb
<div
  id="posts"
  data-controller="pagination"
  data-pagination-url-value="<%= posts_path %> "
  data-pagination-page-value="<%= 2 %>">
  <%= render @posts %>
</div>

Let's look at what the controller does now.

.

When to stop?

Obviously, an scroll should be infinite while there are records to fetch, if there are no more records we must not fetch anymore records. With our current implementation our code would send infinite requests as long the user is scrolling to the end. Let's change that.

Inside app/views/products/index.turbo_stream.erb add this

<%= turbo_stream.append "posts" do %>
  <%= render @posts %>

  <% if @posts.page(@page.to_i + 1).out_of_range? %>
    <span class="hidden" data-pagination-target="lastPage"></span>
  <% end %>
<% end %>

Let's add a little bit code into our stimulus controller.

  scroll() {
    if (this.scrollReachedEnd && !this.hasLastPageTarget) {
      this._fetchNewPage()
    }
  }

we check, if there is a lastPage target present, then we stop fetching new page. This would only be true when there are no more pages left.

  <% if @posts.page(@page.to_i + 1).out_of_range? %>
    <span class="hidden" data-pagination-target="lastPage"></span>
  <% end %>

Bonus, add button to load data instead of infinite scroll

Sometimes, you would like only for when a button pressed to load the data, not when the user reaches end of scrolling. Extending the controller is easy, let's perform just that. Inside pagination_controller add these

   static values = {
        url: String,
        page: Number,
        scroll: Boolean
  };


  connect() {
     if(!this.scrollValue) return; // return and don't attach the scroll event listener
     document.addEventListener("scroll", this.scroll);
  }

  async paginate(e) {
    await this._fetchNewPage();
    e.target.blur();
  }

the new scroll boolean will determine if we should infinite-scroll or not. Change the content of app/views/posts/index.html.erb to the following

<div
  data-controller="pagination"
  data-pagination-url-value="<%= posts_url %> "
  data-pagination-page-value="<%= 2 %>"
  data-pagination-scroll-value="false"
  style="overflow-y: scroll">

  <div id="posts">
    <%= render @posts %>
  </div>

  <button data-action="click->pagination#paginate">
    Load more
  </button>

</div>

Now, let's look at the behaviour

Conclusion

We've first created and configured the dependencies and installed them. After that, we introduced our Stimulus pagination controller to aid us to paginate items. Then, we added a target that indicates we are on the last page, to stop the browser from sending infinite useless requests once we are in the last page. Finally, we've added another way to use the controller, that is, by clicking a button the next page shall load.

Thanks for your reading, hope it helps you in a way. Happy coding!

You can also clone the repo here

Links


This content originally appeared on DEV Community and was authored by Ahmad khattab


Print Share Comment Cite Upload Translate Updates
APA

Ahmad khattab | Sciencx (2021-10-17T19:51:56+00:00) Infinite Scroll with HOTWire – Part 2: Adding Stimulus. Retrieved from https://www.scien.cx/2021/10/17/infinite-scroll-with-hotwire-part-2-adding-stimulus/

MLA
" » Infinite Scroll with HOTWire – Part 2: Adding Stimulus." Ahmad khattab | Sciencx - Sunday October 17, 2021, https://www.scien.cx/2021/10/17/infinite-scroll-with-hotwire-part-2-adding-stimulus/
HARVARD
Ahmad khattab | Sciencx Sunday October 17, 2021 » Infinite Scroll with HOTWire – Part 2: Adding Stimulus., viewed ,<https://www.scien.cx/2021/10/17/infinite-scroll-with-hotwire-part-2-adding-stimulus/>
VANCOUVER
Ahmad khattab | Sciencx - » Infinite Scroll with HOTWire – Part 2: Adding Stimulus. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/10/17/infinite-scroll-with-hotwire-part-2-adding-stimulus/
CHICAGO
" » Infinite Scroll with HOTWire – Part 2: Adding Stimulus." Ahmad khattab | Sciencx - Accessed . https://www.scien.cx/2021/10/17/infinite-scroll-with-hotwire-part-2-adding-stimulus/
IEEE
" » Infinite Scroll with HOTWire – Part 2: Adding Stimulus." Ahmad khattab | Sciencx [Online]. Available: https://www.scien.cx/2021/10/17/infinite-scroll-with-hotwire-part-2-adding-stimulus/. [Accessed: ]
rf:citation
» Infinite Scroll with HOTWire – Part 2: Adding Stimulus | Ahmad khattab | Sciencx | https://www.scien.cx/2021/10/17/infinite-scroll-with-hotwire-part-2-adding-stimulus/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.