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>«</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>»</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.