Task Pagination (#38)

This commit is contained in:
konrad 2019-12-03 18:09:12 +00:00
parent 99cc06edee
commit 2302a46d9b
4 changed files with 93 additions and 7 deletions

View file

@ -58,7 +58,7 @@
}, },
watch: { watch: {
// call again the method if the route changes // call again the method if the route changes
'$route': 'loadList' '$route.path': 'loadList'
}, },
methods: { methods: {
loadList() { loadList() {

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="loader-container" :class="{ 'is-loading': listService.loading}"> <div class="loader-container" :class="{ 'is-loading': listService.loading || taskCollectionService.loading}">
<form @submit.prevent="addTask()"> <form @submit.prevent="addTask()">
<div class="field is-grouped"> <div class="field is-grouped">
<p class="control has-icons-left is-expanded" :class="{ 'is-loading': taskService.loading}"> <p class="control has-icons-left is-expanded" :class="{ 'is-loading': taskService.loading}">
@ -21,8 +21,8 @@
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<div class="tasks" v-if="this.list.tasks && this.list.tasks.length > 0" :class="{'short': isTaskEdit}"> <div class="tasks" v-if="tasks && tasks.length > 0" :class="{'short': isTaskEdit}">
<div class="task" v-for="l in list.tasks" :key="l.id"> <div class="task" v-for="l in tasks" :key="l.id">
<span> <span>
<div class="fancycheckbox"> <div class="fancycheckbox">
<input @change="markAsDone" type="checkbox" :id="l.id" :checked="l.done" style="display: none;"> <input @change="markAsDone" type="checkbox" :id="l.id" :checked="l.done" style="display: none;">
@ -69,6 +69,19 @@
</div> </div>
</div> </div>
</div> </div>
<nav class="pagination is-centered" role="navigation" aria-label="pagination" v-if="taskCollectionService.totalPages > 1">
<button class="pagination-previous" @click="loadTasks(currentPage - 1)" :disabled="currentPage === 1">Previous</button>
<button class="pagination-next" @click="loadTasks(currentPage + 1)" :disabled="currentPage === taskCollectionService.totalPages">Next page</button>
<ul class="pagination-list">
<template v-for="(p, i) in pages">
<li :key="'page'+i" v-if="p.isEllipsis"><span class="pagination-ellipsis">&hellip;</span></li>
<li :key="'page'+i" v-else>
<router-link :to="{name: 'showList', query: { page: p.number }}" :class="{'is-current': p.number === currentPage}" class="pagination-link" :aria-label="'Goto page ' + p.number">{{ p.number }}</router-link>
</li>
</template>
</ul>
</nav>
</div> </div>
</template> </template>
@ -81,6 +94,7 @@
import EditTask from './edit-task' import EditTask from './edit-task'
import TaskModel from '../../models/task' import TaskModel from '../../models/task'
import PriorityLabel from './reusable/priorityLabel' import PriorityLabel from './reusable/priorityLabel'
import TaskCollectionService from '../../services/taskCollection'
export default { export default {
data() { data() {
@ -88,7 +102,11 @@
listID: this.$route.params.id, listID: this.$route.params.id,
listService: ListService, listService: ListService,
taskService: TaskService, taskService: TaskService,
taskCollectionService: TaskCollectionService,
pages: [],
currentPage: 0,
list: {}, list: {},
tasks: [],
isTaskEdit: false, isTaskEdit: false,
taskEditTask: TaskModel, taskEditTask: TaskModel,
newTaskText: '', newTaskText: '',
@ -107,11 +125,14 @@
watch: { watch: {
theList() { theList() {
this.list = this.theList this.list = this.theList
} this.loadTasks(1)
},
'$route.query': 'loadTasksForPage', // Only listen for query path changes
}, },
created() { created() {
this.listService = new ListService() this.listService = new ListService()
this.taskService = new TaskService() this.taskService = new TaskService()
this.taskCollectionService = new TaskCollectionService()
this.taskEditTask = null this.taskEditTask = null
this.isTaskEdit = false this.isTaskEdit = false
}, },
@ -128,6 +149,48 @@
message.error(e, this) message.error(e, this)
}) })
}, },
loadTasks(page) {
this.taskCollectionService.getAll({listID: this.$route.params.id}, {}, page)
.then(r => {
this.$set(this, 'tasks', r)
this.$set(this, 'pages', [])
this.currentPage = page
for (let i = 0; i < this.taskCollectionService.totalPages; i++) {
// Show ellipsis instead of all pages
if(
i > 0 && // Always at least the first page
(i + 1) < this.taskCollectionService.totalPages && // And the last page
(
// And the current with current + 1 and current - 1
(i + 1) > this.currentPage + 1 ||
(i + 1) < this.currentPage - 1
)
) {
// Only add an ellipsis if the last page isn't already one
if(this.pages[i - 1] && !this.pages[i - 1].isEllipsis) {
this.pages.push({
number: 0,
isEllipsis: true,
})
}
continue
}
this.pages.push({
number: i + 1,
isEllipsis: false,
})
}
})
.catch(e => {
message.error(e, this)
})
},
loadTasksForPage(e) {
this.loadTasks(e.page)
},
markAsDone(e) { markAsDone(e) {
let updateFunc = () => { let updateFunc = () => {
// We get the task, update the 'done' property and then push it to the api. // We get the task, update the 'done' property and then push it to the api.

View file

@ -18,6 +18,9 @@ export default class AbstractService {
update: '', update: '',
delete: '', delete: '',
} }
// This contains the total number of pages and the number of results for the current page
totalPages = 0
resultCount = 0
///////////// /////////////
// Service init // Service init
@ -96,7 +99,7 @@ export default class AbstractService {
useDeleteInterceptor() { useDeleteInterceptor() {
return true return true
} }
///////////////////// /////////////////////
// Global error handler // Global error handler
/////////////////// ///////////////////
@ -298,13 +301,16 @@ export default class AbstractService {
* The difference between this and get() is this one is used to get a bunch of data (an array), not just a single object. * The difference between this and get() is this one is used to get a bunch of data (an array), not just a single object.
* @param model The model to use. The request path is built using the values from the model. * @param model The model to use. The request path is built using the values from the model.
* @param params Optional query parameters * @param params Optional query parameters
* @param page The page to get
* @returns {Q.Promise<any>} * @returns {Q.Promise<any>}
*/ */
getAll(model = {}, params = {}) { getAll(model = {}, params = {}, page = 1) {
if (this.paths.getAll === '') { if (this.paths.getAll === '') {
return Promise.reject({message: 'This model is not able to get data.'}) return Promise.reject({message: 'This model is not able to get data.'})
} }
params.page = page
const cancel = this.setLoading() const cancel = this.setLoading()
model = this.beforeGet(model) model = this.beforeGet(model)
return this.http.get(this.getReplacedRoute(this.paths.getAll, model), {params: params}) return this.http.get(this.getReplacedRoute(this.paths.getAll, model), {params: params})
@ -312,6 +318,9 @@ export default class AbstractService {
return this.errorHandler(error) return this.errorHandler(error)
}) })
.then(response => { .then(response => {
this.resultCount = Number(response.headers['x-pagination-result-count'])
this.totalPages = Number(response.headers['x-pagination-total-pages'])
if (Array.isArray(response.data)) { if (Array.isArray(response.data)) {
return Promise.resolve(response.data.map(entry => { return Promise.resolve(response.data.map(entry => {
return this.modelGetAllFactory(entry) return this.modelGetAllFactory(entry)

View file

@ -0,0 +1,14 @@
import AbstractService from './abstractService'
import TaskModel from '../models/task'
export default class TaskCollectionService extends AbstractService {
constructor() {
super({
getAll: '/lists/{listID}/tasks',
})
}
modelFactory(data) {
return new TaskModel(data)
}
}