JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.0/vuex.js"></script>
<div id="app">
<v-pagination v-model="page" :num-pages="numPages"></v-pagination>
<v-loader v-if="loading"></v-loader>
<v-posts v-else :posts="posts"></v-posts>
</div>
CSS
body {
font-family: monospace;
}
.post {
border: 1px solid black;
margin: 5px 0;
padding: 5px;
}
.post-title {
margin: 5px 0;
}
.pagination button:not(:disabled) {
color: red;
font-weight: bold;
}
JavaScript
const store = new Vuex.Store({
state: {
page: 0,
perPage: 5,
total: 0,
posts: [],
loading: false,
},
getters: {
numPages: state => Math.ceil(state.total / state.perPage),
},
mutations: {
updateLoading: (state, loading) => state.loading = loading,
updatePosts: (state, { posts, total, page }) =>
Object.assign(state, { posts, total, page }),
},
actions: {
async fetchPosts({ state, commit }, page) {
commit('updateLoading', true);
const start = (page - 1) * state.perPage;
const end = page * state.perPage;
const url = `https://jsonplaceholder.typicode.com/posts?_start=${start}&_end=${end}`;
try {
const response = await fetch(url);
const posts = await response.json();
const total = response.headers.get('x-total-count');
commit('updatePosts', { posts, total, page });
} catch (e) {
console.error(e);
}
commit('updateLoading', false);
},
},
});
Vue.component('v-loader', {
template: `<div>loading...</div>`,
});
Vue.component('v-posts', {
template: `
<div>
<div v-for="n in posts" :key="n.id" class="post">
<h3 class="post-title">#{{ n.id }} {{ n.title }}</h3>
<div>{{ n.body }}</div>
</div>
</div>`,
props: [ 'posts' ],
});
Vue.component('v-pagination', {
template: `
<div class="pagination">
<button @click="goTo(1)" :disabled="isFirst"><<</button>
<button @click="next(-1)" :disabled="isFirst"><</button>
{{ page }} / {{ numPages }}
<button @click="next(+1)" :disabled="isLast">></button>
<button @click="goTo(numPages)" :disabled="isLast">>></button>
</div>`,
model: {
prop: 'page',
event: 'paginate',
},
props: [ 'page', 'numPages' ],
computed: {
isFirst() {
return this.page <= 1;
},
isLast() {
return this.page >= this.numPages;
},
},
methods: {
goTo(page) {
this.$emit('paginate', page);
},
next(step) {
...