Created
June 18, 2020 19:25
-
-
Save mdwheele/57d75b5857558248fb0cc8b7a4d73929 to your computer and use it in GitHub Desktop.
Example of a renderless component for implementing Pagination
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script> | |
/** | |
* This is a "renderless component". What that means is that it has no presentational | |
* aspect to it at all. It's really just about behaviour and state management. | |
* | |
* It receives a list of whatever you want via :items and allows the consumer to set | |
* a maximum page size via :perPage. Everything from that point forward is under control | |
* of the parent of Paginate. You provide your own slot contents and destructure slot props | |
* to get the current page or interact with pagination. | |
*/ | |
export default { | |
name: 'Paginate', | |
props: { | |
items: { | |
type: Array, | |
default: () => [], | |
required: true, | |
}, | |
perPage: { | |
type: Number, | |
required: false, | |
default: 50, | |
} | |
}, | |
data() { | |
return { | |
currentPage: 1 | |
} | |
}, | |
computed: { | |
numPages() { | |
return Math.ceil(this.items.length / this.perPage) | |
}, | |
hasNext() { | |
return this.currentPage < this.numPages | |
}, | |
hasPrev() { | |
return this.currentPage > 1 | |
}, | |
paginatedItems() { | |
return this.items.slice(this.currentPage * this.perPage - this.perPage, this.currentPage * this.perPage) | |
} | |
}, | |
methods: { | |
/** | |
* Go to an arbitrary page. | |
* | |
* If value of :page is outside the range of 1 - numPages, it's | |
* clamped to the appropriate min/max value. | |
*/ | |
go(page) { | |
// Clamp value between 1 and numPages. | |
this.currentPage = Math.min(Math.max(page, 1), this.numPages); | |
}, | |
/** | |
* Go to the next page. | |
* | |
* If on the last page, stay on the last page. | |
*/ | |
next() { | |
this.currentPage = Math.min(this.currentPage + 1, this.numPages) | |
}, | |
/** | |
* Go to the previous page. | |
* | |
* If on the first page, stay on the first page. | |
*/ | |
prev() { | |
this.currentPage = Math.max(this.currentPage - 1, 1) | |
}, | |
/** | |
* Go to the first page. | |
*/ | |
first() { | |
this.currentPage = 1 | |
}, | |
/** | |
* Go to the last page. | |
*/ | |
last() { | |
this.currentPage = this.numPages | |
} | |
}, | |
/** | |
* This component has no real template. It's expected to wrap other components | |
* or markup to provide transparent pagination behaviour. | |
* | |
* What follows is equivalent to <slot :page="..." :items="..."></slot> if this | |
* component needed a template. Using render functions in this way isn't something | |
* you do everyday, but can be useful from time to time. | |
*/ | |
render(createElement) { | |
return createElement('div', [ | |
this.$scopedSlots.default({ | |
page: { | |
current: this.currentPage, | |
total: this.numPages, | |
size: this.perPage, | |
hasNext: this.hasNext, | |
hasPrev: this.hasPrev, | |
go: this.go, | |
next: this.next, | |
prev: this.prev, | |
first: this.first, | |
last: this.last | |
}, | |
items: this.paginatedItems | |
}) | |
]) | |
} | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment