[Vuejs]-Impossible to build vuejs app – pagination error

0👍

Using v-if and v-for together is not recommended, see this link: https://v2.vuejs.org/v2/guide/conditional.html#v-if-with-v-for

You must replace it for a computed property.

Or you can try somethig like this:

<li class="page-item" v-for="(item, index) in onlineCams" :key="index">
  <template v-if="index >= perpage * (n-1) && index < perpage * n">
    <a class="page-link" href="#">{{ index }}</a>
  </template>
</li>

PS – Answer of your comment:
If you want to create a simple pagination, you can see this code as example:

<template>
  <div>
    <div>{{ itemsPaginate }}</div>
    <div>
      <button @click="currentPage++">next page</button>
      <button @click="currentPage--">prior page</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
  data: () => ({
    items: [
      { id: 1, name: 'Test' },
      { id: 2, name: 'Test' },
      { id: 3, name: 'Test' },
      { id: 4, name: 'Test' },
      { id: 5, name: 'Test' },
      { id: 6, name: 'Test' },
      { id: 7, name: 'Test' },
      { id: 8, name: 'Test' },
      { id: 9, name: 'Test' },
      { id: 10, name: 'Test' },
    ],
    currentPage: 1,
    itemsByPage: 3,
  }),
  computed: {
    itemsPaginate() {
      let result = [];
      let init = (this.currentPage - 1) * this.itemsByPage;
      for (let i = init; i < this.items.length; i++) {
        result.push(this.items[i]);
        if (result.length >= this.itemsByPage) {
          break;
        }
      }
      return result;
    },
  },
};
</script>

If you need the others options you can add.

0👍

Probbably, you’ve mixed Typescript with pure JS.
Some fixes:

  <div
        class="col-1 text-center p-0 feed-col"
        v-for="(cam, idx) in onlineCams"
        :key="idx"
      >
        <img class="img-fluid w-100 h-100" :src="cam.image_url_360x270" />
        <div class="card-img-overlay p-0">
          <a
            class="text-white text-decoration-none stretched-link"
            target="_blank"
            :href="cam.chat_room_url_revshare"
          ></a>
        </div>
        <nav aria-label="Page navigation example">
          <ul class="pagination" v-for="n in pages" :key="n">
            <li class="page-item" v-for="(item, index) in onlineCams" :key="index" v-if="index >= perpage * (n-1) && index < perpage * n">
              <a class="page-link" href="#">{{ index }}</a>
            </li>
          </ul>
        </nav>
      </div>
<script lang="ts">
import Vue from 'vue'; // Added this line

export default Vue.extend({
  name: "Index",
  data() {
    return {
      onlineCams: [],
      perPage: 50
    };
  },
  mounted() {
      // other code for update data here
  },
  computed: {
    // cam pagination
    length() {
      return this.onlineCams.length
    },
    pages() {
      return Math.ceil( this.length / this.perPage )
    }
  },
  methods: { /* other code to fetch data here */ }
}); // end vue
</script>
```

0👍

Here is an example of full client-side pagination, filtering and sorting which you can adapt for your needs:

//
new Vue({
  el: '#app',
  data() {
    return {
      items: [],
      search: '',
      sort: {
        col: 'id',
        desc: false
      },
      paging: {
        page: 1,
        pages: [],
        perPage: 5,
        totalItems: 0,
        totalItemsFiltered: 0,
        next: function() {
          this.page++
        },
        back: function() {
          --this.page
        },
      },
    }
  },
  watch: {
    'paging.perPage': function() {
      this.computePaging()
    },
    items: function() {
      this.computePaging()
    },
  },
  computed: {
    filtereditems() {
      let items = this.items.filter(item => {
        return (
          item.title.toLowerCase().indexOf(this.search.toLowerCase()) > -1 ||
          item.body.toLowerCase().indexOf(this.search.toLowerCase()) > -1
        )
      })
      return (this.paging.page != -1 ?
        this.paginate(items, this.paging.perPage, this.paging.page) :
        items
      ).sort((a, b) => {
        if (a[this.sort.col] < b[this.sort.col]) return this.sort.desc ? -1 : 1
        if (a[this.sort.col] > b[this.sort.col]) return this.sort.desc ? 1 : -1
        return 0
      })
    },
  },
  mounted() {
    this.getItems()
  },
  methods: {
    getItems() {
      //https://jsonplaceholder.typicode.com/posts
      fetch("https://jsonplaceholder.typicode.com/posts", {
          "headers": {
            "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
            "accept-language": "en-GB,en-US;q=0.9,en;q=0.8",
            "cache-control": "max-age=0",
          },
          "method": "GET",
          "mode": "cors",
          "credentials": "include"
        }).then(response => response.json())
        .then(data => this.items = data);
    },
    computePaging() {
      //
      this.paging.pages = []
      this.paging.totalItems = this.items.length
      //
      for (
        let i = 1; i <=
        Math.ceil(
          (this.paging.totalItemsFiltered =
            this.items.filter(item => {
              return (
                item.title.toLowerCase().indexOf(this.search.toLowerCase()) > -1 ||
                item.body.toLowerCase().indexOf(this.search.toLowerCase()) > -1
              )
            }).length / this.paging.perPage)
        ); i++
      ) {
        this.paging.pages.push(i)
      }
    },
    paginate(array, page_size, page_number) {
      --page_number
      return array.slice(page_number * page_size, (page_number + 1) * page_size)
    },
    setSort(col) {
      this.paging.page = 1
      this.sort = {
        col,
        desc: this.sort.col === col ? !this.sort.desc : false
      }
    }
  }
});
/*ignore - hide snippets console */

.as-console-wrapper {
  max-height: 0px !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.14/vue.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

<div id="app">

  <input class="form-control" v-model="search" placeholder="Filter posts..." />

  <table class="table table-sm">
    <tr>
      <th @click="setSort('id')">
        <span v-if="this.sort.col === 'id'">{{ sort.desc ? '▲' : '▼'}}</span>ID
      </th>
      <th @click="setSort('userId')">
        <span v-if="this.sort.col === 'userId'">{{ sort.desc ? '▲' : '▼'}}</span>User Id
      </th>
      <th @click="setSort('title')">
        <span v-if="this.sort.col === 'title'">{{ sort.desc ? '▲' : '▼'}}</span>Title
      </th>
      <th @click="setSort('body')">
        <span v-if="this.sort.col === 'body'">{{ sort.desc ? '▲' : '▼'}}</span>Body
      </th>
    </tr>
    <tr v-for="(item, index) in filtereditems" :key="index">
      <td>{{ item.id }}</td>
      <td>{{ item.userId }}</td>
      <td>{{ item.title }}</td>
      <td>{{ item.body }}</td>
    </tr>
  </table>

  <div class="row no-gutters" style="background-color: #fafafa;">
    <div class="col p-2">
      <div class="form-group">
        Per Page:
        <select class="custom-select ml-1" v-model="paging.perPage" style="width:100px">
          <option>5</option>
          <option>10</option>
          <option>25</option>
          <option>50</option>
          <option>100</option>
        </select>
      </div>
    </div>
    <div class="col p-2">
      <ul class="pagination float-right" v-if="items.length > 0">
        <li class="page-item pagination-prev" :class="{'disabled': paging.page == 1 }">
          <a class="page-link" href="javascript:void(0)" @click="() => { paging.back() }">
            <span>&laquo;</span>
            <span class="sr-only">Previous</span>
          </a>
        </li>
        <template v-for="pageNumber in paging.pages">
              <li
                class="page-item"
                v-if="Math.abs(pageNumber - paging.page) < 5 || pageNumber === paging.pages.length || pageNumber === 1"
                :class="{
                active: paging.page === pageNumber,
                last: (pageNumber === paging.pages.length && Math.abs(pageNumber - paging.page) > 5),
                first:(pageNumber === 1 && Math.abs(pageNumber - paging.page) > 5)
              }"
                @click="() => { paging.page = pageNumber }"
                :key="'pn-'+pageNumber"
              >
                <a class="page-link" href="javascript:void(0)">{{ pageNumber }}</a>
              </li>
            </template>
        <li class="page-item pagination-next" :class="{'disabled': paging.page === paging.pages.length}">
          <a class="page-link" href="javascript:void(0)" @click="() => { paging.next() }">
            <span>&raquo;</span>
            <span class="sr-only">Next</span>
          </a>
        </li>
      </ul>
    </div>
  </div>
</div>

Ideally, if you can implement paging, sorting and filtering server-side its much simpler then as the app can simply set params like ?page=1&sort=id&desc=1&search=abc then you only need run computePaging on items array which will calculate the pagination dom.

Leave a comment