Simple Pagination in Ember ArrayControllers
One of the projects that I have been working on recently had a (fairly common) requirement to paginate a list of news items. The original controller and route looked something like this:
1 2 3 4 5 6 7 | |
This is pretty simple, and it’s obviously making use of Ember.SortableMixin
to sort each news item in reverse chronological order.
So the first thing we need is a two properties which will specify the current page and the number of items to show per page:
1 2 3 4 | |
Straight forward enough. Now we need a computed property that contains only the news items for the current page:
1 2 3 4 5 6 7 8 9 | |
But wait, what’s happened here? We’ve lost our sorting! A quick read of
the Em.SortableMixin source
shows us that the property arrangedContent is where the sorted contents of
our controller are stored, so let’s change our code to use that property instead:
1 2 3 4 5 6 7 8 9 | |
In order to build our pagination links, we probably want to know how many pages
are available also, so let’s add a pages property:
1 2 3 4 5 6 7 | |
“But James!” you cry, “you forgot to depend on arrangedContent.” Nope.
The number of items doesn’t change when the sort order changes, so we forgo
the cost of recalculating the number of pages if the controller changes the
sort order.
This is pretty cool, but let’s extract this into a mixin so that we can re-use this in other ArrayControllers:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
One thing to note about the paginatedContent property is that it might not
be very performant on very large collections - because it creates a new array
every time the original content is changed. That’s because we’re creating a
simple Ember.computed instead of an Ember.arrayComputed to handle adding
and removing elements without iterating the entire collection. This
functionality will be appearing (alongside a handy paginate() function) soon in
an updated version of ember-enumerology
as a result of a bunch of peer-coding with David J. Hamilton.
Thanks David!
So now we have our pagination taken care of, we need to show our pagination links. This is an excellent use-case for Ember’s Components.
Let’s start by creating a pagination-links component, to house all our
controls. First a template using simple Bootstrap controls
(templates/components/pagination-links.handlebars):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
So, this is pretty straight forward, and we’ve discovered that in order to dry
up our template we need an additional component, which I’ve called
pagination-page.
At this point I’d like to point out the fact that Ember requires you to to have at least one
-in the name of your component, thus we have a component namedpagination-linksinstead of justpaginate.
Our pagination-page template looks pretty simple:
1
| |
Wait, where’s the <li>? We’ll tell our component to call itself li instead
of div as having spurious divs lying around will interfere with Bootstrap’s
CSS.
1 2 3 4 | |
So now our pagination-page component will appear inside an li tag, with a
class of disabled if the page we’re linking to is the current page.
Actions triggered on a component do not bubble up. This makes sense from a certain point of view (in that components are actually just views with a little sugar on top), however this certainly violated the principle of least surprise for me.
Since actions don’t bubble from within components, my initial plan of leaving
the pageClicked action to the PaginationLinksComponent to deal with won’t
work. So we need to teach the PaginationPageComponent how to deal with it:
1 2 3 4 5 | |
The trick here is that the enclosing component (in this case pagination-links)
is really the parentView of our component, we can call send on it to send
an action.
Next lets start implmenting our PaginationLinksComponent. Let’s start by
doing the simplest properties first:
1 2 3 4 5 | |
This is pretty cool. What about visiblePages? This is a slightly complicated
function, it needs to:
- If there are more than five pages, then only show five pages to link to.
- If there are less than five pages, then only show the pages there are.
- Center the list of pages around the current page, unless that would show negative page numbers, or numbers greater than the total number of pages.
Let’s give this a go:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
The only other properties we need now are our show{Before,After}Ellipsis
properties. These are pretty straight forward now that we know which pages
are being shown:
1 2 3 4 | |
Cool, now we can insert our pagination-links component into our news template:
1 2 3 4 5 | |
Lastly, we need to add actions to our PaginationLinksComponent:
1 2 3 4 5 6 7 8 9 10 11 | |
A note on performance: loading a large collection from your server into the client as JSON isn’t going to be terribly performant, and because we’ve not used
arrayComputedEmber isn’t able to show incremental results as that data streams in. This solution works fine for smaller datasets, but when the amount of data grows you will need to look at ways of loading the data in a paginated way also. This should be do-able without rewriting any of our pagination code.
Well, we’ve come to the end of our little exploration of building pagination with Ember. There’s been a couple of niggles along the way, but we got there. I hope this comes in handy to others, and I’d love to read any comments you may have.

