Skip to content

Instantly share code, notes, and snippets.

@mdwheele
Created June 18, 2020 19:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mdwheele/57d75b5857558248fb0cc8b7a4d73929 to your computer and use it in GitHub Desktop.
Save mdwheele/57d75b5857558248fb0cc8b7a4d73929 to your computer and use it in GitHub Desktop.
Example of a renderless component for implementing Pagination
<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