Inertia

Filter records with infinite scrolling using Vue and Inertia

Hewo, I have this code where infinite scroll is implemented in an app with Laravel, Vue and Inertia using the IntersectionObserve API.

On the back-end side everything is set up as usual, but I'm very lost on how to make it work on the front-end using Vue and Inertia router.

I've tried so many things, like watchers, hooks, reset the items... but by default it only works fine if the page is reloaded completely and not dynamically by typing, where it has a weird behaviour where the url of the Inertia page is not updated correctly, it is one letter late, here is an example of use case: if I type: sa, it only takes the results for s, and when I retype, for example: sap, then it takes the results for sa.

I understand that this may be because the ref items are not updated when refetching, but I don't know how I could do it either.

The files are as follows:

/// Index.vue

<script setup>
import { ref, watch } from 'vue'
import { router } from '@inertiajs/vue3'
import { pickBy, debounce } from 'lodash'
import ListItem from '@/Components/ListItem.vue'
import Filters from '@/Components/Filters.vue'
import { useInfiniteScroll } from '../../Composables/useInfiniteScroll'

const props = defineProps({
  recipes: {
    type: Object,
    default() {
      return {}
    },
  },
  filters: {
    type: Object,
    default: null,
  },
})

const params = ref({
  filters: {
    search: props.filters.search,
  },
})

const landmark = ref(null)

let { items, canLoadMoreItems, resetItems } = useInfiniteScroll('recipes', landmark)

let refetch = debounce(function () {
  router.get('/recipes', pickBy({ ...params.value.filters }), {
    replace: true,
    preserveState: true,
  })
}, 200)

watch(params, () => {refetch(), resetItems() }, { deep: true })
</script>

<template>
<!-- ... -->
  <section class="hidden 2xl:block 2xl:w-full">
    <Filters v-model="params.filters.search" />
  </section>
  <ListItem
    v-for="recipe in items" />
<!-- ... -->
</template>
// useInfiniteScroll.vue composable

import { computed, ref } from 'vue'
import { router, usePage } from '@inertiajs/vue3'
import { useIntersect } from '@/Composables/useIntersect'

export function useInfiniteScroll(propName, landmark = null) {
  const value = () => usePage().props[propName]

  const items = ref(value().data)

  const initialUrl = usePage().url

  const canLoadMoreItems = computed(() => value().next_page_url !== null)

  const loadMoreItems = () => {
    if (!canLoadMoreItems.value) {
      return
    }

    router.get(
      value().next_page_url,
      {},
      {
        preserveState: true,
        preserveScroll: true,
        onSuccess: () => {
          window.history.replaceState({}, '', initialUrl)
          items.value = [...items.value, ...value().data]
        },
      },
    )
  }

  if (landmark !== null) {
    useIntersect(landmark, loadMoreItems, {
      rootMargin: '0px 0px 150px 0px',
    })
  }

  return {
    items,
    loadMoreItems,
    resetItems: () => (items.value = value().data),
    canLoadMoreItems,
  }
}

Thanks so much for reading I appreciate any help ~

d3lus10n
d3lus10n
0
1
252
Haz
Haz
Moderator

Hello,

Do you have a simplified repo that you can share? Maybe I can take a look.

Have you tried watching the filtering videos from this course:

https://codecourse.com/courses/build-a-forum-with-inertia-and-laravel

It might help shed some light on the issue.

I also think that infinite scrolling isn't the best approach when you need to apply multiple filters. Things can get a little tricky.