Using Vue for Firestore pagination

Utilizing the bootstrap-vue pagination component:

<b-pagination
  v-model="currentPage"
  :total-rows="rows"
  :per-page="perPage"
></b-pagination>

Component.vue:

export default class PaginatedLinks extends Vue {
  public currentPage: number = 1
  public perPage: number = 20
  public rows: number = 1

  @Watch('currentPage')
  onCurrentPageChange(page: number) {
    const startAt = page * this.perPage - this.perPage
    db.collection('links')
      .orderBy('created', 'desc')
      .startAt(startAt)
      .limit(this.perPage)
      .get()
      .then(snap => {
        console.log(snap.docs)
      })
  }
}

After changing the value of currentPage, fetching new DB values with startAt did not return anything. The firestore contains documents with fields:

  • created (date)
  • title (string)
  • url (string)

How can I implement a standard pagination with offsets and limits?

Answer №1

I've encountered a similar issue with pagination before. The documentation provided is lacking in detail, specifically when it comes to navigating back to the previous page. It can be quite frustrating.

Below is my approach to implementing a simple pagination solution. Assuming that VueFire, Firebase, and BootstrapVue have already been set up, let's dive into the code.

Here's what no one tells you to do differently:

  • Utilize programmatic binding in VueFire instead of declarative binding (refer to this link)
  • To retrieve the first visible item in firebase, use documentSnapshots.docs[0]
<template>
    <div>
        <p>{{countries}}</p>
         <b-button-group size="lg" class="mx-2">
      <b-button :disabled="prev_btn" @click="previous" >&laquo;</b-button>
      <b-button :disabled="next_btn" @click="next">&raquo;</b-button>
    </b-button-group>
    </div>
</template>
<script>
 import firebase from 'firebase/app'
import 'firebase/auth'
import { db } from '../main'

export default {
 name: 'Countries',
 data () {
  return {
   countries: [],
   limit: 2,
   lastVisible: '',
   firstVisible: '',
   next_btn: false,
   prev_btn: true
  }
 },
 methods: {
  next () {
   if (!this.next_btn) {
   // bind data with countries
    this.$bind('countries', db.collection('Countries').orderBy('createdAt').startAfter(this.lastVisible).limit(this.limit))
    // set last and first visible items
    db.collection('Countries').orderBy('createdAt').startAfter(this.lastVisible).limit(this.limit).get().then(documentSnapshots => {
     this.lastVisible = documentSnapshots.docs[documentSnapshots.docs.length - 1]
     this.firstVisible = documentSnapshots.docs[0]
    }).then(() => {
     // Peep  on the next  next query to see if it gives zero
     db.collection('Countries').orderBy('createdAt').startAfter(this.lastVisible).limit(this.limit).get()
      .then(snap => {
       if (snap.size === 0) {
        //disable button if the next peeped result gets zero
        this.next_btn = true
        // enable previous button
        this.prev_btn = false
       } else {
        // enable next button if peeped result is not zero
        this.next_btn = false
        // enable previous button
        this.prev_btn = false
       }
      })
    })
   }
  },
  previous () {
   // Ensure previous is not zero
   db.collection('Countries').orderBy('createdAt').endBefore(this.firstVisible).limitToLast(this.limit).get().then(snap => { return snap.size })
   .then(size => {
    //confirm is not zero here
    if (size !== 0) {
     //bind the previous to countries
     this.$bind('countries', db.collection('Countries').orderBy('createdAt').endBefore(this.firstVisible).limitToLast(this.limit))
     // Set last and first visible
     db.collection('Countries').orderBy('createdAt').endBefore(this.firstVisible).limitToLast(this.limit).get().then(documentSnapshots => {
      this.lastVisible = documentSnapshots.docs[documentSnapshots.docs.length - 1]
      this.firstVisible = documentSnapshots.docs[0]
     }).then(() => {
      // peep the next previous query
      db.collection('Countries').orderBy('createdAt').endBefore(this.firstVisible).limitToLast(this.limit).get()
       .then(snap => {
        if (snap.size === 0) {
         //if next peeped previous button gets 0 disable
         this.prev_btn = true
         this.next_btn = false
        } else {
         //if next peeped result is does not get 0 enable buttons
         this.prev_btn = false
         this.next_btn = false
        }
       })
     })
    }
   })
  }
 },
 mounted () {
  // run first query and bind data
  this.$bind('countries', db.collection('Countries').orderBy('createdAt').limit(this.limit))
  // set last and first Visible
  db.collection('Countries').orderBy('createdAt').limit(this.limit).get().then(documentSnapshots => {
   this.lastVisible = documentSnapshots.docs[documentSnapshots.docs.length - 1]
   this.firstVisible = documentSnapshots.docs[0]
  }).then(() => {
            // peep to check if next should be on or off
            db.collection('Countries').orderBy('createdAt').startAfter(this.lastVisible).limit(this.limit).get()
                .then(snap => {
                    if (snap.size === 0) {
                        this.next_btn = true
                    }
                })
        })
 }
}
</script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue with jQuery sortable serialization when dynamically adding content is not being recognized

I've been attempting to re-order a list through ajax on sortable update. However, when I add a new item to the list via ajax after the sortable has already been initialized upon page load, it fails to recognize the new item with the "serialize" functi ...

Adding an item to a JSON array in Angular

I'm having trouble adding a new item to the roleList JSON array. I've tried using push/concat, but it doesn't seem to update the roleList array. Any suggestions on how to resolve this? // The javascript : function RoleListCtrl($scope) { ...

Having trouble getting the default NextJS template to work with TailwindCSS

Sysinfo: > Windows 11 > Node: v18.16.0 > Next: 13.4.13 > Tested Browser: Firefox, Chrome. Step to Reproduce To recreate the issue, I created a NextJS project using the command npx create-next-app tezz with specific options selected: Would you ...

Encountered error message: "Cannot assign argument of type '() => () => boolean' to parameter of type 'EffectCallback'"

I recently started working with TypeScript. I encountered an issue when attempting to utilize useEffect in TypeScript within a React context, Error: Argument of type '() => () => boolean' is not assignable to parameter of type 'Effec ...

A Guide to Organizing Vue Js: Dividing 'methods', 'data', 'computed', and More into Separate JavaScript Files

Is it feasible to separate methods, data, computed properties, etc. into individual .js files and then import them into a component.vue file? I prefer not to include all JavaScript logic in a single .vue component. My ideal code organization for each com ...

Using MySQL data in an EJS file to create a personalized Profile Page

In the midst of a personal project aimed at honing my web development skills and gaining a deeper understanding of the intricacies of the field, I have hit a roadblock. The focus of this project is to create a profile page, and while I have successfully co ...

What is the process for closing the lightbox?

My code is supposed to display a lightbox as soon as the page loads, similar to a popup window. However, there seems to be an issue with closing the lightbox when clicking on the 'x' button at the corner of the box. Unfortunately, the current cod ...

What are the steps for conducting a component test with material ui?

My current component is built using . import React from 'react'; import { AppBar, Toolbar } from 'material-ui'; import { Typography } from 'material-ui'; import { MuiThemeProvider, createMuiTheme } from 'material-ui/sty ...

Performing a Javascript validation to ensure that a value falls within

How can I check if an input number falls within a specified range? I need to display if it is within the range, higher, or lower than the acceptable range. My current Javascript code seems to be causing an error message stating it is an invalid entry. It ...

Positioning of Responsive Slider

I'm currently working on a responsive website, but I am facing challenges with the placement of the slideshow dots. When I switch to the device toolbar, they seem to change position. I have tried various methods such as using relative and absolute uni ...

Utilizing i18n for moment formatting techniques

As I switch from moment to date-fns, I came across this code that involves moment, which I don't quite understand. However, I do comprehend the map function. this.events[0].dates.map(date => moment(date).format(this.$i18n.locale)) I set up an e ...

Different ways to categorize and tally a collection of objects

I'm having trouble reshaping my data in order to create a stacked bar graph. The data from the backend is structured like this: [ {date: 'January', category: 'A'}, {date: 'January', category: 'B'}, ...

Utilizing ES6 JavaScript for Creating Static Methods and Angular 2 Services

During the development of an Angular 2 app involving multiple calculation services, I encountered some interesting questions: Is it beneficial to use static in an Angular service provided on the application level? Or is it unnecessary? How does a static ...

How can I modify all URLs in .htaccess to point to index.html while still preserving existing directories, since I am utilizing Vue-Router?

I created a web application using VUE CLI with Vue-Router functionality. In order to rewrite all requests to the index.html file, I added the following code: RewriteEngine On RewriteRule ^(.*)$ ./index.html [L] However, there is an issue with two folders ...

Missing transitive dependencies have been identified within the node_modules directory when using npm

In my software development journey, I am working on two npm projects - one called X and the other called Y. Let's say that X relies on angular and has it specified as a dependency in its package.json file. To establish a connection between X and Y, I ...

Manipulate HTML Elements with a Click of a Button in React

Is there a straightforward method to replicate this jQuery example using only React, without relying on jQuery or other external libraries? I'm still developing my ReactJS skills and hoping for guidance on dynamically creating and deleting elements. ...

ReactJS how to prevent accordion from collapsing automatically

My custom accordion layout for the features needed in the site I am building is not working well with Jquery. How can I modify it to collapse properly? Specifically, I want it so that when I open number 2, number 1 will automatically close. I've trie ...

Determine the Placement of Bootstrap Popover by Utilizing SVG and Mouse Coordinates

Is there a way to dynamically reposition the popover that currently appears to the right of the Circle SVG? I would like to either base its position on the user's mouse click within the CircleSVG or move it to the exact center of the SVG, including bo ...

Retrieve the parent node's class using JavaScript without relying on jQuery

Attempting to retrieve the class of the parent node of a selected element using JavaScript, but encountering issues with the following code snippet. Any guidance on what needs to be corrected? function getParentNodeClass() { var parentNodeClass = this.p ...

Tips for showing a single progress message when uploading multiple files on eleme.io [vuejs]

Tech Stack: Utilizing Vuejs with element.eleme.io framework. Objective: To implement a feature that allows users to upload multiple files while displaying only one "in progress message". To handle image uploads, we are integrating . During the upload pr ...