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:
- 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.
- 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).