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