Vuejs application experiencing pagination issues during development

I am encountering an issue while attempting to paginate an array within my vue.js app component. Upon building it, an error is displayed in the vue ui output console:

warning  Replace `·class="page-item"·v-for="(item,·index)·in·onlineCams"·:key="index"·v-if="index·>=·perpage·*·(n-1)·&&·index·<·perpage·*·n"` with `⏎··············class="page-item"⏎··············v-for="(item,·index)·in·onlineCams"⏎··············:key="index"⏎··············v-if="index·>=·perpage·*·(n·-·1)·&&·index·<·perpage·*·n"⏎············`  prettier/prettier
  193:84  error    The 'onlineCams' variable inside 'v-for' directive should be replaced with a computed property that returns filtered array instead. You should not mix 'v-for' with 'v-if'

The provided code is not working as expected. How can I resolve this issue?

Below is the HTML markup for pagination:

      <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>

Here is the JS code snippet:

<script>
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>

Answer №1

Perhaps you've mistakenly mixed Typescript with pure JS.
Here are some corrections:

  <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 updating data will be here
  },
  computed: {
    // camera pagination
    length() {
      return this.onlineCams.length
    },
    pages() {
      return Math.ceil( this.length / this.perPage )
    }
  },
  methods: { /* other code to retrieve data goes here */ }
}); // end vue

Answer №2

Below is a comprehensive example of how to achieve client-side pagination, filtering, and sorting using Vue.js:

//
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>

If possible, implementing server-side paging, sorting, and filtering results in a simpler app structure, where the application sets parameters like

?page=1&sort=id&desc=1&search=abc
. This allows for running computePaging on the items array to calculate pagination on the client side.

Answer №3

It is not advisable to use v-if and v-for together, for more information check out this link: https://v2.vuejs.org/v2/guide/conditional.html#v-if-with-v-for

Instead, consider using a computed property.

Alternatively, you can experiment with something like the following:

<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 - Response to your comment: For a basic pagination, you can refer to the following code:

<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>

You can add more options if needed.

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

Is there a way to display the button only when the user is able to enter an email in the input field?

I have a form for users to update their profile and a button to delete their account. I want the delete account button to only be visible if the user enters their email. $(document).ready(function() { var user_email = $('input[name="emp_email"]&a ...

Error Loading JQuery: The function $() is not recognized in the Shopify platform

I seem to be overlooking something obvious. I am using Shopify and have included jQuery in the head section of the theme.liquid file: <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> This script is placed rig ...

How big is the array size in the WebAudio API data?

Exploring the visualization of waveform and FFT generated by the audio stream from the microphone through the WebAudio API. Curiosity strikes - what is the size of each data array available at a given moment? Delving into the getByteTimeDomainData, it men ...

The ajax response is being deserialized prior to being parsed by the

In my API controller, I need to respond with a model that includes a decimal property. However, the response type is simply a string. I am aware that converting large decimal data to a JavaScript number may result in some data loss. Therefore, I am consi ...

Is the conditional module require within an "If" statement always executed in nuxt.config.js?

Despite the missing file mod.json, the "require" statement is still executed leading to an error. The condition should have prevented it from entering the "if" block: const a = 1; if(a === 2){ const mod = require('./scripts/mod ...

The continuous looping issue is being triggered when implementing a like button using firestore along with useEffect and UseState

I have developed a component to be loaded into various cards for displaying data. This particular component retrieves and stores data from the card (sale_id) onto the database. import { LikeButtonStyle } from './LikeButton.styled'; import { Image ...

What could be causing my default prop to not be transmitted to the child component in vuejs2?

Having trouble passing a default value to my Leaflet map child component before fetching the desired data from an API endpoint. I tried using country coordinates like latitude and longitude, but it's not working as expected. This is how I attempted t ...

How can I change :hover to a clickable element instead?

I attempted to create a full-width accordion with the following code: .page { margin: 0; padding: 0; display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; height: 100vh; } .content { -webkit- ...

Transform a log file into a JSON structure

In my log file titled request.log, the following entries are present: [2022-06-30T09:56:40.146Z] ### POST https://test.csdf/auth/send_otp { "method": "POST", "headers": { "User-Agent": "testing&q ...

Using the event object in the onClick handler within React applications

In my React app, I have implemented a feature where clicking on a column heading sorts the data in the table using merge sort algorithm My goal is to pass both the data (an array of objects) and the event object to the sorting function. However, I am faci ...

We have successfully implemented version x3 validation using Netlify forms

Currently, I have a functional form that submits using Netlify forms, making the process straightforward by simply adding name="contact" data-netlify="true" method="POST". This setup handles all the backend work seamlessly. The basic structure of my form ...

Tips for displaying just the image name without the entire image file path

When I try to upload an image, the path displayed is "C:\fakepath\promotion1.JPG". This fake path is causing the image not to upload to my project media folder. How can I solve this issue? Instead of the complete path, I only want to capture the ...

Exploring each item within oData: A guide

After writing the code statement above, I am able to see the attached image. Now, my challenge is accessing the "label" property inside each object. How can I iterate through these objects and retrieve their "label" properties? item.getModel().oData; I a ...

Any ideas on how I can use PHP or JavaScript to repeatedly execute a segment of HTML code?

I recently tried using a for loop and heredoc in PHP with code that looks something like this: $options = ''; for($Year = date("Y"); $Year <= date("Y") + 5; $Year++) { $options .= "<option>$Year</option>\n"; } $Select = ...

Creating custom elements for the header bar in Ionic can easily be accomplished by adding your own unique design elements to the header bar or

I'm a beginner with Ionic and I'm looking to customize the items on the header bar. It appears that the header bar is created by the framework within the ion-nav-bar element. <ion-nav-bar class="bar-positive"> <ion-nav-back-button> ...

Variables in an Angular application are not appearing in the HTML rendering

Currently learning AngularJS as I develop a web application. After going through tutorials, I started with a basic list but am baffled as to why my browser only shows {{title}}. In my app.js module, here's the snippet where I defined the controller: ...

What is the best way to include an ID parameter in a $ajax GET request?

Real-life Dilemma I've been grappling with an issue for the past 4 hours - I'm attempting to send an http get request with the user ID as a parameter. Despite trying numerous examples found online, I continue to encounter this pesky error on the ...

Accessing CSV data stored externally using JavaScript

Hi there, I am struggling to load external CSV data into a script due to what I believe is the browser's same origin policy restriction. I came across some information about using cross-document messaging as a workaround, but I have no idea how to go ...

Overflow and contain a flex item within another flex item

I am facing a challenge where I need to prevent a flex-child of another flex-child from overflowing the parent, ensuring it does not exceed the screen height or the height of the parent (which is also a flex-child). Here's the CodePen link for refere ...

Tips for interacting with a custom web component using Selenium WebDriver

As a newcomer to writing selenium tests, I am attempting to create an automated test for a carousel feature on our homepage. The objective is to click on one of the carousel navigation buttons and then confirm that a specific inline style has been applied ...