kaminari
.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
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.
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 !!
Then, I would like to go according to the image I mentioned earlier.
This time it will be posts/index.html.slim
because it will be reflected on the post list screen.
.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.
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.
QuerySelector
and getElementbyId
methods in JS..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.
You can see that it has #posts
! With this, the place to be replaced by asynchronous processing can be specified.
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.
How to use querySelector method [Introduction to JavaScript] 3 tips to fully understand getElementById!
[Introduction to JavaScript] How to get and set elements in div tags with innerHTML
Recommended Posts