DEV Community

Dominic Myers
Dominic Myers

Posted on • Originally published at drmsite.blogspot.com on

VueJS Pagination

I've written about pagination before, and it's something I'm interested in so when the chance came to write a component that dealt with it at work I jumped at the chance. Rather than using any of the other techniques I've used in the past, this component was using VueJS, and I knew I'd get some data via a message bus. The data was in the form of an object with just three numbers: the current page selected, the total number of items and the number of items for display on each page.

The total number of items could range from one to many; the number of pages could also be massive depending upon the page size selected by the user.

As well as wanting to display a sensible number of page numbers to the user, I also wanted to decorate the pagination. The decoration needed to have the ability to jump three pages back and forth as well as a quick way of navigating to the first and last page.

So I did what I usually do and fired up JSFiddle to work up a quick prototype and came up with this. I've not included the message bus, but I'm instead using values within the data of the component. While I know it's not good form to alter the data within the component; I'm doing so here for illustration purposes.

Logic splits between the markup and the JS, but I quite like that for some reason.

HTML

<div id="app">
  <ol v-if="totalPages > 0"
      aria-label="Pagination navigation">
    <li v-on:click="paginationOptions.page = 1"             
        aria-label="Go to the first page"             
        title="Go to the first page"            
        v-bind:class="{                
          'disabled': paginationOptions.page === 1            
        }">
      <div class="content">&larr;</div>
    </li>
    <li v-if="totalPages > 3 && paginationOptions.page - 2 >= 1"             
        v-on:click="paginationOptions.page = (paginationOptions.page - 3 < 1) ? 1 : paginationOptions.page - 3"             
        aria-label="Jump three pages backward"             
        title="Jump three pages backward">
      <div class="content"></div>
    </li>
    <li v-for="page in pages"             
        v-bind:key="page"             
        v-on:click="paginationOptions.page = page"             
        v-bind:aria-label="(page === paginationOptions.page) ? 'Current page, page ' + page : 'Go to page ' + page"             
        v-bind:aria-current="page === paginationOptions.page"             
        v-bind:title="(page === paginationOptions.page) ? 'Current page, page ' + page : 'Go to page ' + page"            
        v-bind:class="{                
          'active': page === paginationOptions.page            
        }">
      <div class="content">{{page}}</div>
      <span v-if="page === paginationOptions.page"                   
            style="display: none">(current)</span>
    </li>
    <li v-if="totalPages > 3 && paginationOptions.page + 2 <= totalPages"             
        v-on:click="paginationOptions.page = (paginationOptions.page + 3 <= totalPages) ? paginationOptions.page + 3 : totalPages"             
        aria-label="Jump three pages forward"             
        title="Jump three pages forward">
      <div class="content"></div>
    </li>
    <li v-on:click="paginationOptions.page = totalPages"             
        aria-label="Go to the last page"             
        title="Go to the last page"            
        v-bind:class="{                
          'disabled': paginationOptions.page === totalPages            
        }">
      <div class="content">&rarr;</div>
    </li>
  </ol>
</div>
Enter fullscreen mode Exit fullscreen mode

JS

new Vue({ 
  el: "#app", 
  data: { 
    paginationOptions: { 
      page: 2, 
      total: 55, 
      pageSize: 10
    }
  }, 
  computed: { 
    totalPages() {
      return Math.ceil(this.paginationOptions.total / this.paginationOptions.pageSize);
    }, 
    pages: function () {
      const returnArray = [];
      if(this.paginationOptions.page === 1) {
        for(i = 1, count = 0; i <= this.totalPages && count < 3; i++, count++){ 
          returnArray.push(i)
        }
      } else {
        if(this.paginationOptions.page === this.totalPages) {
          for(let i = this.totalPages, count = 0; i >= 1 && count < 3; i--, count++) { 
            returnArray.push(i)
          } 
          returnArray.reverse();
        } else { 
          returnArray.push(this.paginationOptions.page);
          if(this.paginationOptions.page < this.totalPages) { 
            returnArray.push(this.paginationOptions.page + 1)
          }
          if(this.paginationOptions.page >= 1){ 
            returnArray.unshift(this.paginationOptions.page - 1)
          }
        }
      }
      return returnArray;
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

SCSS

#app{ 
  margin: 1em;
}
ol { 
  display: flex; 
  align-items: center; 
  justify-content: flex-end; 
  margin: 0; 
  padding: 0;
  list-style-type: none; 
  width: 100%; 
  text-align: right; 
  li{ 
    display: inline-block; 
    &.disabled { 
      cursor: not-allowed; 
    } 
    .content { 
      text-decoration: none; 
      font-weight: bold; 
      background-color: #fff;
      color: #7f8c8d; 
      display: flex; 
      align-items: center; 
      justify-content: center; 
      width: 40px; 
      height: 40px; 
      margin: 0 3px; 
      border: 1px solid #e5e5e5; 
    } 
    &.active { 
      .content { 
        border-color: #2c3e50; 
        background-color: #2c3e50;
        color: #fff; 
        text-decoration: none; 
      } 
    } 
  }
}
Enter fullscreen mode Exit fullscreen mode

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

Top comments (0)

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay