javascriptvue.jsnuxt.js

How to make an infinite scroll/loader in Nuxt?


hello i want display data after infinite loading my data send by asyncData to page and the id page like this

Page

<script>
export default {
  async asyncData({ $axios, store }) {
    const customerId = store.getters['user/auth/customerId']
    if (!customerId) {
      return
    }
    const products = await customerApi.getProducts(
      { $axios },
      customerId,
      this.page
    )
    return {
      products,
    }
  },
  data() {
    return {
      page: 1,
    }
  },
}
</script>

I have the data of the first page. products is the props data send in my template. in my template i add a infinite loading

template

<template>
  <generic-button v-if="!viewMore" inline @click="viewMore = true">
    see more
  </generic-button>
  <client-only v-else>
    <infinite-loading
      :distance="800"
      force-use-infinite-wrapper
      @infinite="infiniteHandler"
    ></infinite-loading>
  </client-only>
</template>

<script>
export default {
  components: {
    InfiniteLoading: () =>
      process.client
        ? import('vue-infinite-loading')
        : Promise.resolve({ render: (h) => h('div') }),
  },
  props: {
    products: {
      type: Array,
      required: true,
    },
  },
  data() {
    return {
      page: 1,
    }
  },
  methods: {
    infiniteHandler($state) {
      // the method that will look for the data when the pagination changes
    },
  },
}
</script>

I would like that when the pagination is started that it relaunches the asyncData request by changing the page parameter to +1 and displays the data

thank you


Solution

  • Really not a huge fan of the vue-infinite-loading package but I still managed to make it work.

    Here is the end result with the Parent/Child relation that you wanted.

    parent page

    <template>
      <div>
        <nuxt-link :to="{ name: 'redirect' }">
          Go to another page and come back to have this one triggered
        </nuxt-link>
    
        <child ref="child" :users="users" @fetchMore="callApi"></child>
      </div>
    </template>
    
    <script>
    export default {
      name: 'ParentPage',
      async asyncData({ $axios }) {
        const { data } = await $axios.$get(
          'https://reqres.in/api/users?per_page=2&page=1'
        )
        return { users: data }
      },
      methods: {
        async callApi(newPageAsked) {
          const { data: newUsers } = await this.$axios.$get(
            `https://reqres.in/api/users?per_page=2&page=${newPageAsked}`
          )
          console.log('new users fetched? ', newUsers)
    
          if (newUsers.length) {
            // if API got some new users, add them to the current list
            this.users = [...this.users, ...newUsers]
            // tell `infinite-loading` component that the fetch was successful
            this.$refs.child.$refs.infiniteLoader.stateChanger.loaded()
          } else {
            // if the API do not have anymore users to fetch
            this.$refs.child.$refs.infiniteLoader.stateChanger.complete()
          }
        },
      },
    }
    </script>
    

    Child.vue

    <template>
      <div class="small-height">
        <div v-for="user in users" :key="user.id">
          <p class="user">
            <span>{{ user.first_fame }}</span>
            <span>{{ user.last_name }}</span>
            <br />
            <span>{{ user.email }}</span>
            <br />
            <img :src="user.avatar" />
          </p>
        </div>
    
        <infinite-loading
          ref="infiniteLoader"
          @infinite="infiniteHandler"
        ></infinite-loading>
      </div>
    </template>
    
    <script>
    import InfiniteLoading from 'vue-infinite-loading'
    
    export default {
      name: 'Child',
      components: {
        InfiniteLoading,
      },
      props: {
        users: {
          type: Array,
          default: () => [],
        },
      },
      data() {
        return {
          page: 1,
        }
      },
      methods: {
        infiniteHandler($state) {
          this.page += 1
          this.$emit('fetchMore', this.page)
        },
      },
    }
    </script>
    
    <style scoped>
    .small-height {
      height: 200px;
      border: 2px solid red;
      width: 400px;
      overflow-y: auto;
    }
    .user {
      height: 200px;
    }
    </style>
    

    I've added comments and if you inspect the Network tab, you should have enough info to see what is happening.
    I've used reqres.in to mock a paginated API.