Tạo Dữ liệu Lazy Load với bắt sự kiện scroll kết hợp gem will_paginate trong RoR
Bài đăng này đã không được cập nhật trong 6 năm
1. Giới thiệu về load dữ liệu theo kiểu lazy load:
- Nếu bạn là một người hay mua sắm online trên mạng ví dụ như trang Lazada chẳng hạn, thì chắc mọi người cũng biết về nút tải thêm hay khi scroll xuống dòng thì sẽ load thêm dữ liệu. Đó được gọi là tải dữ liệu theo kiểu lazy load, nói đơn giản hơn là nếu bạn có dữ liệu lên đến hàng chục, trăm nghìn record thì nếu bạn dùng cách thông thường thì sẽ làm trang web chạy rất lâu và người dùng sẽ cảm thấy khó chịu, vì vậy load dữ liệu lazy load ra đời
2. Gem will_paginate:
- Thì gem will_paginate là một gem giúp cho ứng dụng ta phân trang, đây cũng là một dạng lazy load vì thay vì load hết tất cả record trong một trang thì chúng ta sẽ phân ra thành nhiều trang và mỗi trang sẽ chỉ chứa bao nhiêu record mà chúng ta muốn.
- Nếu bạn chưa biết gì về gem này thì có thể tham khảo link: https://github.com/mislav/will_paginate hoặc bạn cũng có thể dùng các gem hỗ trợ phân trang khác cũng được như gem
Kaminari
chẳng hạn
3. Áp dụng:
- Đương nhiên điều đầu tiên thì là ta phải có app rails rồi và cách tạo chắc mình cũng không cần nói nhiều nữa.
- Sau khi tạo xong trong
Gemfile
ta thêm các gem sau:gem "will_paginate"
: hỗ trợ phân tranggem "jquery-rails"
: hỗ trợ ta viết javascript theo jquerygem "Faker"
: tạo ra các record giả
- Sau khi chạy bundle xong, ta dùng hai câu lệnh này để tạo ra controller cũng như model
rails g controller posts
: Tạo ra file controller tên làposts_controller.rb
và folder view tênposts
rails g model post
: Tạo ra model file tênpost
và file migratemã_create_post.rb
(mã ở đây là gồm dãy số tập hợp ngày tháng năm giờ phút giây mà bạn khởi tạo file migrate này). Trong file này tạo ra hai trường title và content
- Sau khi đã có những thứ cơ bản: Trong file
seed.rb
ta tạo dữ liệu giả gồm 100 bài posts
Chạy100.times do |n| Post.create! ( title: "Post #{n}", content: Faker::Lorem.sentence 5 ) end
rails db:seed
hayrake db:seed
để tạo - Trong file controller ta thêm:
@posts = Post.select(:id, :title, :content).order(created_at: :desc) .paginate page: params[:page], per_page: 10 respond_to do |format| format.html format.js end
- Trong view ta tạo file:
- index.html.erb và thêm:
<div class="page-header"> <h1>My posts</h1> </div> <div id="my-posts"> <%= render @posts %> </div> <div id="infinite-scrolling"> <%= will_paginate %> </div>
- _posts.html.erb và thêm:
<div> <h2><%= link_to post.title, post_path(post) %></h2> <p><%= post.content %></p> </div>
- index.js.erb (file này được dùng khi có một request js gửi lên controller để xử lý thì sau đó nó sẽ chạy tới file này dựa trên đoạn lệnh
respond_to
mà mình viết ở trên để thực hiện những câu lệnh js và vì là file có đuôi erb nên việc gửi dữ liệu từ ruby là bình thường) ta thêm đoạn lệnh này vào:$('#my-posts').append('<%= j render @posts %>'); <% if @posts.next_page %> $('.pagination').replaceWith('<%= j will_paginate @posts %>'); <% else %> $(window).off('scroll'); $('.pagination').remove(); <% end %>
- Đoạn lệnh này có nghĩa từ thẻ có id là
my-post
bên file html ta append (tức là thêm ở cuối) thêm ''<%= j render @posts %>' (tức thêm các giữ liệu tiếp theo ở cuối). Câu lệnhif
là để bắt sự kiện nếu@post.next_page
thì ta replace lại phân trang và ngược lại thì đóng scroll và xóa phân trang đi (ở đây là khi load hết tất cả dữ liệu và không còn gì để load nữa thì xóa nó đi)
- index.html.erb và thêm:
- Và thêm một điều quan trọng cuối cùng đó là bắt sự kiện khi chúng ta scroll xuống dòng. Trong folder app/assets/javascripts ta tạo file scroll.js và thêm đoạn lệnh
$(document).on('turbolinks:load', function () { $(window).on('scroll', function(){ var more_posts_url = $('.pagination .next_page a').attr('href'); if (more_posts_url && $(window).scrollTop() > $(document).height() - $(window).height() - 100) { $.getScript(more_posts_url); } }); });
- Ở đây đoạn javascript này đang bắt sự kiện khi bạn scroll thì nó sẽ lấy đường link từ
$('.pagination .next_page a').attr('href')
gán vào biếnmore_posts_url
và xác định điều kiện nếumore_posts_url
tồn tại và$(window).scrollTop()
lớn hơn$(document).height() - $(window).height() - 100)
thì sẽ gửirequest js
tới đường link trongmore_posts_url
. và như mình đã nói phía trên thì ở controller sẽ xử lý điều đó. - Ở id là
infinite-scrolling
của thẻ div chứa phân trang bạn có thể ẩn nó đi bằng cách viết trong file scss hoặc css là:#infinite-scrolling { display: none; }
- Ở đây đoạn javascript này đang bắt sự kiện khi bạn scroll thì nó sẽ lấy đường link từ
Kết:
Trên đây mới chỉ là kiểu lazy load đơn giản, sau bài này mình sẽ viết thêm một bài về load dữ liệu trên modal bằng lazy load, mong mọi người ủng hộ. Link tham khảo: https://www.sitepoint.com/infinite-scrolling-rails-basics/
All rights reserved