0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

v-data-tableを利用して無限スクロールテーブルComponentを作成する

Posted at

はじめに

無限スクロール機能を作成する機会があったためメモを残すことにしました。
本当はnuxt-property-decoratorを使って作って見たかったのですが、今回は断念しました。

Component

今回はdocumentではなくtableにスクロールイベントを持たせる作りにしています。
また、apiからページネートされたデータを取得できる前提で作成しています。

MyInfinityScrollDataTable.vue
<template>
  <v-data-table
    v-bind="$attrs"
    ref="MyInfinityScrollDataTable"
    :loading="loading"
    :loading-text="loadingText"
    fixed-header
    disable-sort
    disable-pagination
    hide-default-footer
  >
    <!-- v-data-table slot and scopedSlots -->
    <slot
      v-for="(_, name) in $slots"
      :slot="name"
      :name="name"
    />
    <template
      v-for="(_, name) in $scopedSlots"
      :slot="name"
      slot-scope="slotData"
    >
      <slot :name="name" v-bind="slotData" />
    </template>
    <!-- loading sign -->
    <template #foot>
      <tr
        v-if="!isLastPage"
        class="text-center"
      >
        <td
          class="--loading-text text-subtitle-2 my-2"
          colspan="7"
        >
          <v-divider />
          <span
            v-for="(text, i) of loadingTextArray"
            :key="i"
            :style="{ animationDelay: `${i * 0.1}s` }"
            class="py-3"
            :class="{ 'ml-1': text.startsWith(' ') }"
          >
            {{ text }}
          </span>
        </td>
      </tr>
    </template>
  </v-data-table>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  name: 'MyInfinityScrollDataTable',
  props: {
    loading: {
      type: Boolean,
      default: false,
    },
    loadingText: {
      type: String,
      default: 'Loading... Please wait',
    },
    isLastPage: {
      type: Boolean,
      default: true,
    },
  },
  computed: {
    loadingTextArray(): string[] {
      return this.loadingText?.match(/.{1,4}/g) || []
    },
    tableInstance(): Element {
      const table = this.$refs.MyInfinityScrollDataTable as Vue
      return (table?.$el?.childNodes || [])[0] as Element
    },
  },
  mounted() {
    this.tableInstance.addEventListener('scroll', this.onScroll)
  },
  methods: {
    onScroll() {
      if (this.loading) {
        return
      }

      const { scrollTop, clientHeight, scrollHeight } = this.tableInstance
      if (scrollTop + clientHeight < scrollHeight) {
        return
      }

      if (this.isLastPage) {
        return
      }

      setTimeout(() => {
        this.$emit('next')
      }, 1000)
    },
  },
})
</script>

<style lang="scss" scoped>
.--loading-text {
  background: var(--v-secondary-lighten3);
  color: var(--v-secondary-base);

  span {
    display: inline-block;
    animation: loading 1.4s infinite alternate;
  }
}

@keyframes loading {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}
</style>


使用側

このソースでは、無限スクロールを実現するに当たって、pageではなくper_pageを制御する方針を取ります。
これは、更新用のUIとして使用されることを想定した場合に、更新後データのrefetchをし易くするためです。
データ量が多くなる場合は、この制御は見直す必要があると思われます。

以下は製造時の都合でvuexを利用した作りになっています

template
  <infinity-scroll-data-table
    :height="items.length > 4 ? 300 : undefined"
    :headers="headers"
    :items="items"
    :server-items-length="items.length"
    :loading="loading"
    :is-last-page="isLastPage"
    @next="fetchList(perPage + 20)"
  />
script
  data(): Data {
    loading: false,
  },
  computed: {
    isLastPage(): boolean {
      // store getterでlast_pageを取得する
      return lastPage === 1
    },
    perPage(): number {
      // store getterでper_pageを取得する
    },
    items(): Item[] {
      // store getterでデータリストを取得する
    },
  },
  methods: {
    async fetchList (perPage: number = 20): void {
      this.loading = true

      // (await) store actionで新しいper_pageでデータリストをサーバから取得してstateに詰める

      this.loading = false
    },
  }

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?