Madogiwa Blog

主に技術系の学習メモに使っていきます。

railsとVueを使って無限スクロール機能を実装するMEMO🌀

自分が作っているSPAっぽいrailsのサービスでrailsとVueで無限スクロール∞を作ったので、そのやり方をメモしておきますm( )m

つくるもの

下記のようにスクロール時にAPIでデータを取得して表示していくような機能を作っていきます🌀

f:id:madogiwa0124:20190303213625g:plain

使うもの

今回は無限スクロールの導入に、vue-infinite-loadingを使いました🙌
結構チュートリアルも充実してて使いやすかったです👀

peachscript.github.io

実際のコード

今回は、つくるもので紹介したような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の中でlimitoffsetを使ってページングを考慮してデータを取得しています📖

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に感謝🙏

参考

peachscript.github.io

www.shookuro.com