自分が作っているSPAっぽいrailsのサービスでrailsとVueで無限スクロール∞を作ったので、そのやり方をメモしておきますm( )m
つくるもの
下記のようにスクロール時にAPIでデータを取得して表示していくような機能を作っていきます🌀
使うもの
今回は無限スクロールの導入に、vue-infinite-loading
を使いました🙌
結構チュートリアルも充実してて使いやすかったです👀
実際のコード
今回は、つくるもので紹介したようなFeedのCardのコンポーネントをリスト表示するような機能の実装で説明していきます。
View
<main class="column"> <feed-card-collection /> </main> <%= javascript_pack_tag 'boards/new' %>
ポイントは、<infinite-loading @infinite="infiniteHandler" />
で∞スクロールのコンポーネントを入れてあげることと、infiniteHandler
の中で、api呼び出し及びfeedのリストに結果を追加していく部分です👀
<template> <div class="entries is-multiline columns"> <page-loader :init_is_loading="isLoading" /> <div v-for="feed in feeds" :key="feed.id" class="column is-4" > <feed-card :feed="feed" :lastEntry="feedLastEntry(feed)" /> </div> <infinite-loading @infinite="infiniteHandler" /> </div> </template> <script> import FeedCard from './FeedCard'; import InfiniteLoading from 'vue-infinite-loading'; import axios from 'axios'; const feedsApi = '/api/feeds'; export default { name: 'FeedCardCollection', components: { FeedCard, InfiniteLoading }, props: ['init_feeds', 'init_last_entries'], data: function () { return { page: 1, feeds: [], last_entries: [], isLoading: true }; }, mounted: function () { // MEMO: 初回表示時にデータ取得するため実行 this.infiniteHandler(); this.$nextTick(function () { this.isLoading = false; }); }, methods: { feedLastEntry: function(feed) { return this.last_entries.filter(entry => entry.feed_id === feed.id)[0]; }, infiniteHandler($state) { axios.get(feedsApi, { params: { page: this.page }, }).then(({ data }) => { if (data.feeds.length) { this.page += 1; this.feeds.push(...data.feeds); this.last_entries.push(...data.last_entries); $state.loaded(); } else { $state.complete(); } }); }, } }; </script> <style lang="scss"> </style>
Controller
ポイントは、params[:page]
でページ番号を取得出来るので、そちらを使ってpagingを考慮して結果を取得する必要があるので、Model側に追加したpager
を使って取得するようにしている部分です。
class Api::FeedsController < ApplicationController PER_PAGE = 6 def index @feeds = Feed.recent.pager(page: params[:page], per: PER_PAGE) @last_entries = @feeds.includes(:last_entry).map(&:last_entry) object = { feeds: @feeds, last_entries: @last_entries } render json: object end end
Model
スコープpager
の中でlimit
とoffset
を使ってページングを考慮してデータを取得しています📖
class Feed < ApplicationRecord scope :pager, ->(page: 1, per: 10) { num = page.to_i.positive? ? page.to_i - 1 : 0 limit(per).offset(per * num) } end
おわりに
railsとvueを使った無限スクロールの実装方法を書いてみました。
プラグインを使うと結構簡単に実装出来ますね、OSSに感謝🙏