[RUBY] How to make asynchronous pagenations using Kaminari

What to explain in this article

  1. Implement asynchronous pagination using SJR.
  2. The page nation itself uses the gem kaminari.

Briefly explain how to make an app

It is a clone app of Instagram, and it is a mechanism that allows users to post images. It is in the form of user has many posts, and this time I would like to add pagination to Posts. For reference, I can ride the table configuration.

users table

  create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.string "email", null: false
    t.string "crypted_password"
    t.string "salt"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "username", null: false
    t.string "avatar"
    t.index ["email"], name: "index_users_on_email", unique: true
  end

posts table

  create_table "posts", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.string "images", null: false
    t.text "body", null: false
    t.bigint "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["user_id"], name: "index_posts_on_user_id"
  end

Introduce kaminari anyway.

Gemfile

gem 'kaminari'

As usual bundle install

This time, we will add page nation to the posts posted by the user, so change the description of posts_controller.

posts_controller

def index
  @posts = Post.page(params[:page]).includes(:user).order(created_at: :desc)
end

Up to this point, if the table structure is the same, it will be the same whether it is asynchronous or synchronous. Now when you press the pagenation button, it will automatically fetch the corresponding posts. Normally, you can implement normal pagination just by writing paginate @ posts in the view.

However, this time, since it is asynchronous pagination, the implementation will be slightly different from here.

Image of implementation

I will explain the implementation image this time.

If you read the above article, you will understand that asynchronous processing is extremely rough. ** It can be said that only the necessary part is reflected on the screen, not the entire page **.

So if you apply this requirement

① First decide the screen to reflect. This time posts/index.html.slim ② In the file decided in ①. ** Specify ** only the post list part (This time, specify by giving an id.) ③ Replace only the post list part specified earlier from posts/index.html.slim. (I will return the response only for this part)

I would like to implement it with such an image !!

Implementation

Then, I would like to go according to the image I mentioned earlier.

① First decide the screen to reflect

This time it will be posts/index.html.slim because it will be reflected on the post list screen.

Image of the entire page

Image from Gyazo

code

.container
  .row
    #posts.col-md-8.col-12
      = render @posts
      = paginate @posts
    .col-md-4.col-12
      - if logged_in?
        .profile-box.mb-3
          = image_tag 'profile-placeholder.png', size: '50x50', class: 'rounded-circle mr-1'
          = current_user.username
      .users-box
        .card
          .card-header
            |user
          .card-body
            - @dummy_names.each do |name|
              .user.mb-3
                = image_tag 'profile-placeholder.png', size: '40x40', class: 'rounded-circle mr-1'
                = name
          .card-footer
            = link_to 'See all', '#'

This code is the source code that is partially replaced as explained in ①. Let's find out where you want to update.

② Specify only the post list in the image of ①

When I read the code carefully, I found that the post list was cut out as it was. (4th line) The description of pagenation is added in advance on the 5th line.

posts/index.html.slim

.container
  .row
    .col-md-8.col-12
      = render @posts  <=This part is the post list
      = paginate @posts <=Pagination

In other words, you can see that this partial and pagenation part should be replaced quickly by asynchronous communication. So how do you identify these two parts?

In such a case, give id or class and make a guide for the place to replace. This time, we will add a id called # posts to the parent element.

.container
  .row
    #posts.col-md-8.col-12
      = render @posts  <=This part is the post list
      = paginate @posts <=This is page nation

This is just this part.

Image from Gyazo

You can see that it has #posts! With this, the place to be replaced by asynchronous processing can be specified.

③ Replace the post list part specified earlier from posts/index.html.slim.

Up to this point, you have identified the page to be replaced and the location to be replaced in the page. So let's implement asynchronous processing.

Asynchronous processing with kaminari is very easy. You can send a js format request just by adding the familiar remote: true. It's a bit off topic, but after this implementation, let's take a look at the ContentType of the Network tag in the developer tools. remote: true will change this item from html to javascript.

remote:true None
Content-Type: text/html; charset=utf-8

remote:true Yes
Content-Type: text/javascript; charset=utf-8

I took a detour a little, but add remote: true as shown below.

posts/index.html.slim

.container
  .row	  
    #posts.col-md-8.col-1
      = render @posts
      = paginate @posts, remote: true

You can now send JS format requests. Also, the template file that is automatically called when the pagenation button is pressed will also be called action name.js.erb.

It's a little difficult to understand how much to replace it as it is, so I will put these two together in a partial.

posts/_posts_paginate.html.slim

= render posts
= paginate posts, remote: true

posts/index.html.slim


.container
  .row
    #posts.col-md-8.col-12
      = render 'posts_paginate', posts: @posts

This means that you can write the process to replace _posts_paginate.html.slim in index.js.erb.

posts/index.js.erb

var postsPaginate = document.querySelector('#posts'); //Specify the id set here. May work with const
postsPaginate.innerHTML = "<%= j render 'posts_paginate', posts: @posts%> "//Replace the contents with innerHTML.
history.replaceState( "", "" ,"?page=<%= @page %>");//Rewriting the URL so that it can be reloaded

The location is specified on the first line. In other words, it specifies the location of # posts. The second line uses the innerHTML method to swap the child elements. This time the child element is

    #posts.col-md-8.col-12
      = render 'posts_paginate', posts: @posts

Partial = render'posts_paginate', posts: @posts only. In other words, in the second line, only the contents of @ posts are replaced by kaminari with the same contents. In the third line, the URL is rewritten using a method called replaceState to handle the reload. By the way, the contents of @ page are

def index
 #processing of kaminari
 @page = params[:page]
end

is. This is a little off the main line, so I will omit the explanation and just put the link. Memo for manipulating URLs with JavaScript

If you can implement it so far, pagination should work asynchronously.

When it comes to asynchronous processing, it's easy to get hooked at first. ① Specify the place to replace with id. ② If possible, make the place you want to replace partial. (It is easier to replace with the js method) ③ Replace the partial part as it is in js.erb.

Since I started to implement this way of thinking, I have become able to easily implement Rails SJR. I will also post a reference material at the end, so please read it if you like.

Reference material

To identify the place to replace

How to use querySelector method [Introduction to JavaScript] 3 tips to fully understand getElementById!

Method to replace

[Introduction to JavaScript] How to get and set elements in div tags with innerHTML

Recommended Posts

How to make asynchronous pagenations using Kaminari
How to make shaded-jar
Java --How to make JTable
How to make an oleore generator using swagger codegen
[Rails] How to use "kaminari"
[Rails] How to make seed
How to authorize using graphql-ruby
How to make an app using Tensorflow with Android Studio
How to make a Java container
How to make a JDBC driver
How to make a splash screen
How to make a Jenkins plugin
How to make a Maven project
How to make a Java array
[Android] How to make Dialog Fragment
How to build CloudStack using Docker
How to make an image posted using css look like an icon
How to make a groundbreaking diamond using Java for statement wwww
How to execute a contract using web3j
How to sort a List using Comparator
How to make a Java calendar Summary
[Rails] How to upload images using Carrierwave
[Java] How to calculate age using LocalDate
How to make a Discord bot (Java)
[Ruby on Rails] How to use kaminari
[Swift5] How to implement animation using "lottie-ios"
How to implement image posting using rails
How to implement asynchronous processing in Outsystems
[Rails] How to handle data using enum
How to insert icons using Font awesome
How to disable user operations during asynchronous processing
How to output Excel and PDF using Excella
How to execute and mock methods using JUnit
[Rails] How to create a graph using lazy_high_charts
How to make rbenv recognize OpenSSL on WSL
How to delete a controller etc. using a command
How to play audio and music using javascript
[Ethereum] How to execute a contract using web3j-Part 2-
How to create pagination for a "kaminari" array
How to implement the breadcrumb function using gretel
How to deploy
How to make duplicate check logic more readable
How to make Unity Native Plugin (Android version)
[Rails] How to upload multiple images using Carrierwave
How to generate a primary key using @GeneratedValue
Try to make a music player using Basic Player
How to create hierarchical category data using ancestry
How to link images using FactoryBot Active Storage
How to make a follow function in Rails
[Java] How to operate List using Stream API
How to make an crazy Android music player
[Java] How to make multiple for loops single
How to make an image partially transparent in Processing
How to make batch processing with Rails + Heroku configuration
How to make a factory with a model with polymorphic association
How to figure out how much disk Docker is using
How to unit test with JVM with source using RxAndroid
[Rails, JS] How to implement asynchronous display of comments
How to make LINE messaging function made with Ruby
How to make Java unit tests (JUnit & Mockito & PowerMock)
How to add characters to display when using link_to method