element ui のtableにinfinite scroll を付けてみた

はじめに

nuxt.jsでelement uiを使用したテーブルコンポーネントに下記のようなinfinite scrollを実装してみました。

今回使用したライブラリはvue-infinite-loadingを利用しました。

githubリポジトリこちら

環境構築編

$ create-nuxt-app <project-name>

$ cd <project-name>

$ npm install

$ npm install --save vue-infinite-loading element-ui

element uiのコンポーネントを使用できるようにします。

plugins/element-ui.js

import Vue from 'vue'
import 'element-ui/lib/theme-chalk/index.css'
const ElementUI = require('element-ui')

Vue.use(ElementUI)

nuxt.config.js

export default {
  ...
  plugins: [
    { src: '~/plugins/element-ui' }
  ],
  ...
}

pageの実装

次にpageを実装していきます。

pages/index.vue

<template>
  <section>
    <el-button @click="resetInfinite">reset</el-button>
    <div class="table-container">
      <el-table :data="accounts" style="with: 100%">
        <el-table-column prop="id" label="id" />
        <el-table-column prop="name" label="name" />
        <el-table-column prop="email" label="email" width="180" />
      </el-table>
    </div>
  </section>
</template>

<script>
export default {
  data() {
    return {
      accounts: []
    }
  },
  created() {
    for (let i = 0; i < 300; i++) {
      var account = {
        id: i + 1,
        name: `test_${i + 1}`,
        email: `test_${i + 1}@example.com`
      }
      this.accounts.push(account)
    }
  }
}
</script>

いよいよinfinite scrollを実装していきます。

まずはvue-infinite-loadingをimportします。

pages/index.vue

<script>
import InfiniteLoading from 'vue-infinite-loading'

export default {
  components: {
    InfinitLoading
  },
...
</script>

テーブルのはみ出した要素はscrollするようにしましょう。

pages/index.vue

<style>
.table-container > .el-table > ..el-table__body-wrapper {
  overflow: scroll;
  max-height: 480px;
}
</style>

InfiniteLoadingのコンポーネントをel-tableの一番下に追加します。 さらにテーブルで使用するデータをaccountsから変更します。

pages/index.vue

...
    <div class="table-container">
      <el-table :data="list" style="with: 100%">
        <el-table-column prop="id" label="id" />
        <el-table-column prop="name" label="name" />
        <el-table-column prop="email" label="email" width="180" />
        <InfiniteLoading
          slot="append"
          :identifier="infiniteId"
          force-use-in-infinite-wrapper=".el-table__body-wrapper"
          @infinite="infiniteHandler"
        />
      </el-table>
    </div>
...
<script>
...
  data () {
    return {
      accounts: [],
      list: []
    }
  }
}
</script>

スクロールされたら走るinfiniteHandlerを実装しましょう。

...
  data() {
    return {
      accounts: [],
      list: [],
      limit: 15,
      page: 0,
      infiniteId: +new Date()
    }
  },
  created() {
    for (let i = 0; i < 300; i++) {
      var account = {
        id: i + 1,
        name: `test_${i + 1}`,
        email: `test_${i + 1}@example.com`
      }
      this.accounts.push(account)
    }
    this.fetchList(this.limit)
  },
  methods: {
    infiniteHandler($state) {
      if (this.accounts.length && this.accounts.length > this.list.length) {
        let scrollLimit = (this.page + 1) * this.limit
        if (this.accounts.length < scrollLimit) {
          scrollLimit = this.accounts.length
        }
        this.fetchList(scrollLimit)
        $state.loaded()
      } else {
        $state.complete()
      }
    },
    fetchList(scrollLimit) {
      this.list.push(
        ...this.accounts.slice(this.page * this.limit, scrollLimit)
      )
      this.page++
    },
    resetInfinite() {
      this.page = 0
      this.list = []
      this.fetchList(this.limit)
      this.infiniteId++
      const elm = document.getElementsByClassName('el-table__body-wrapper')[0]
      elm.scrollTo({ top: 0 })
    }
  }
}

if (this.accounts.length && this.accounts.length > this.list.length) {}で現在表示されているデータよりもaccountsが多ければinfinite scrollでデータが読み込まれるようになっています。 scrollLimitには何件目までのデータを取得するかが入り、

this.list.push(
  ...this.accounts.slice(this.page * this.limit, scrollLimit)
)

で現在読み込まれているデータ+1番目のデータからscrollLimitまでのデータをlistに追加します。