Skip to content
Advertisement

Vue JS real-time client search filter on paginated Laravel results

I’m having some troubles adding client-side real-time search filtering to my Nuxt JS project that pulls data from my Laravel 8 API. My Laravel 8 API uses the paginate() method to paginate my results from my database and return back some JSON whereby the data array contains all the records for the current page.

My front-end contains a table with a v-for looping over some filtered results provided by filteredDomains which essentially gets the data after a request to my API gets the results.

The issue is, my real-time search on the front-end works, but only for page one, and I’d like to have it filter and search through all of the pages, as if they were one page (which they previously were and this worked before, now that it’s paginated it doesn’t)

What am I missing?

<b-row>
  <b-col>
    <b-form-input
      style="padding-right: 10rem;"
      v-model="search.domain"
      type="text"
      size="lg"
      maxlength="256"
      placeholder="Start typing a domain...">
    </b-form-input>
  </b-col>
</b-row>
<b-row>
  <b-col>
    <div class="table-responsive">
      <table class="table table-bordered table-striped">
        <thead>
          <tr>
            <th scope="col"></th>
            <th scope="col">Domain</th>
            <th scope="col">Registrar</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="(domain, index) in filteredDomains" :key="index" class="domain-row">
            <td class="text-center" style="min-width: 60px;">
              ...
            </td>
            <td :style="domain.has_valid_ssl == 1 ? 'min-width: 260px;' : 'min-width: 150px;'">
              ...
            </td>
            <td style="min-width: 150px;">
              ...
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </b-col>
</b-row>
<b-row>
  <b-col>
    <b-pagination
      v-model="page"
      :total-rows="domains.total"
      :per-page="domains.per_page"
      first-number
      last-number
      pills
    ></b-pagination>
  </b-col>
</b-row>

And my script is:

export default {
  data () {
    return {
      page: this.$route.query.page ? this.$route.query.page : 1,
      hasErrors: false,
      isLoading: true,
      search: {
        domain: ''
      },
      domains: []
    }
  },
  created () {
    this.getDomains()
  },
  methods: {

    /*
    ** Get domains
    */
    getDomains () {
      this.hasErrors = false

      this.$axios.get(`${process.env.API_URL}/api/domains?page=${this.page}`, { timeout: 60000 }).then(res => {
        if (res.data.success) {
          this.isLoading = false
          this.hasErrors = false

          const domains = res.data.domains.data.length > 0 ? res.data.domains : []
          if (domains.data && domains.data.length > 0) {
            for (const [index, domain] of domains.data.entries()) {
              if (typeof domain.registrar != 'string' || domain.registrar == null) continue
              domain.registrar = JSON.parse(domain.registrar)
            }
          }

          this.domains = domains
        }
      }).catch(err => {
        this.isLoading = false
        this.hasErrors = true
      })
    }

    /*
    ** Navigate to a page
    */
    navigateToPage (page) {
      try {
        const goTo = page

        this.page = parseInt(goTo)
        this.$router.push({
          query: {
            page: this.page
          }
        })

        this.getDomains()
      } catch (err) {}
    }

  },
  computed: {

    /*
    ** Filter domains for searchable domains
    */
    filteredDomains () {
      if (!this.domains.data) {
        return []
      }
      return this.domains.data.filter(domain => {
        return domain.domain.toLowerCase().includes(this.search.domain.toLowerCase())
      })
    }

  },
  watch: {
    page (val) {
      this.navigateToPage(this.page)
    }
  }
}

Maybe I need to do a for loop inside of my filteredDomains function to construct a list of all domains and filter this list? This seems overkill though…

Advertisement

Answer

The way you currently use pagination, you only have one page of domains that is returned from the API in your data. By default that will be the first page, so your code behaves as expected.

To fix this, I see two options:

  1. Fetch all the domains and filter those (as you suggested yourself). You can also implement the pagination on the clientside if you want to keep the feature.
  2. Pass your searchterm along to your API. That way the filtering would need to be done on the serverside and just the filtered domains would be returned.

Which one fits your situation better depends on the number of items. If there are relatively few items, I would suggest the first option, as it keeps all the logic in one place. However if there are hundreds or even thousands of items, I would suggest the second option as databases are oftentimes better optimized to do this kind of filtering.

In either case you should look at adding a debounce to help with performance. Without a debounce your data will be searched over and over again after each single letter the user types into the search box. With a debounce, the data will only be filtered, once the user stops typing for a specified amount of time (usually arount 300ms).

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement