[RAILS] Asynchronously output "html element of pagination" with kaminari

If you search for "kaminari asynchronous", you will find a way to display the "linked page" of pagination asynchronously. This is also introduced in the kaminari README. https://github.com/kaminari/kaminari

Instead, it is an introduction of how to asynchronously output the "html element of pagination" later. I didn't find much in the search. image.png (↑ Pagination html element)

Why asynchronous?

Normally, when you use kaminari to display pagination elements, write it as <% = paginate (@users)%> in the view. However, in order to find out what page the last page is, a "query to get the total number of records" is played.

SELECT COUNT(*) FROM users;

In this case, it seems light, but if you add various search conditions, it will be a heavy query. Therefore, the motive is that I want to display the pagination elements asynchronously.

Implementation

First, I will put the whole picture. In Rails, it is assumed to be the index page of the User model.

routes.rb


resources :users

users_controller.rb


def index
  users = User.all.page(params[:page]).per(3)
  respond_to do |format|
    format.html { @users = users.without_count }
    format.json { render :json => view_context.paginate(users).gsub('.json', '') }
  end
end

html:users/index.html.erb


<div id="paginator"></div>
<script>
  document.addEventListener('DOMContentLoaded', () => {
    const path = location.pathname + '.json' + location.search;
    const paginator = document.querySelector('#paginator');
    fetch(path).then(response => response.text()).then(html => {
      paginator.insertAdjacentHTML('afterbegin', html);
    })
  })
</script>

Commentary

The processing flow will be explained in order. First, when an html request comes to the index action of the users controller, add the .without_count method to the model and then pass it to the view.

format.html { @users = users.without_count }

With this method, the "query to get the total number of records" mentioned earlier will not be issued **. An html with no pagination elements is returned and the synchronization process is complete.

From here it is asynchronous. The returned html (in javascript) requests the pagination element asynchronously. At this time, set the URL as location.pathname +'.json' + location.search; Send "URL that just changed the end of the URL path to json". If the original path is / users? Age = 10, the path to send is / users.json? Age = 10.

The request sent is also sent to the index action of the users controller. Since the URL has .json, respond_to is called format.json.

format.json { render :json => view_context.paginate(users).gsub('.json', '') }

view_context is a method that returns an instance of the view (https://apidock.com/rails/ActionView/Rendering/view_context). If you use this, you can use the method of the view from the controller etc., and you can call paginate (user). Since the variable ʻuser used here does not have without_count, a "query to get the total number of records" will be issued **. It is a pagination element created by the paginate method, but if the format is json (other than html), the end of the link will be .json`. So erase it with gsub. Wrap the completed html string in json and return it.

On the received javascript side, just attach it to the appropriate element and you're done.

fetch(path).then(response => response.text()).then(html => {
  paginator.insertAdjacentHTML('afterbegin', html);
})

I was able to output pagination elements asynchronously. (In the example of ↓, sleep is inserted)

a.gif

Good, asynchronous. I hope this article helps someone.

Recommended Posts

Asynchronously output "html element of pagination" with kaminari
Kaminari --Added pagination function of Rails
Create pagination function with Rails Kaminari
Pagination function (kaminari)