Pagination/Sorting/Filtering for a beginner


#1

I am looking for a way to do server-side pagination, sorting and filtering, but in a way that still allows me to persist data client-side so I can take advantage of data-binding.

It seems I have been searching for this type of feature for weeks without finding any solution. Perhaps it comes from a lack of understanding on my part because I am still new to Ember and Javascript in general. I am looking for guidance or references/tutorial that could help me out.

I have found many solutions for paginating, sorting, and filtering, that all seem to work client-side, and my understanding is that all the data needs to be pulled from the server first, before it can work? But how does that work if you have a large amount of data? doesn’t that defeat the purpose of what we want to accomplish?

Am I not understanding something? Can these “client-side” solutions be made to pull records from the server in small chunks and then persist them?

Ember has new query parameters that seem to do what i would need, perhaps in a few cases… but i don’t want to serialize the URL with these queries if I can’t help it, I would prefer to have the functionality be hiding in the background as much as possible.

I’m not looking for a purely ajax method either, that i have already accomplished. I am looking for something of a hybrid that take advantage of Embers models and data binding and such.

Can anyone shed some light for me? Is there anything out there that can do this? Perhaps I am just unclear about the limits of what can and cannot be done.

Thanks!


#2

Typically you would use params with find e.g.

this.store.find('post', { limit: 20, sort: "desc" })

but perhaps this is what you mean by a purely ajax solution? When you say “persist them” are you referring to storing them on the client? If so ember-data will add them to the local store when loaded but it won’t use them when you call find (you can use filter to query loaded models).

ember-orbit is going to offer a lot more flexibility with these sorts of requirements but it’s probably a bit early to be using that in production yet.


#3

This is actually how i started out. But i got lost when I wanted to send new parameters and refresh the route with updated data.

@alexspeller helped me out on #emberjs today and now i have a better idea of what to do.

I had no idea that i could bind properties inside that query params hash, and now I can simply use an action that changes those properties and call this.refresh() to resend the model hook to the store/server as needed.

this is essentially what i have now.

export default Ember.Route.extend({
    pageSize: 25,
    pageNumber: 1,

    model: function() {
        return View.find({
            page_size: this.pageSize,
            page_number: this.pageNumber});
    },

    actions: {
        nextPage: function() {
            this.pageNumber += 1;
            return this.refresh();
        },
        previousPage: function() {
            this.pageNumber -= 1;
            return this.refresh();
        }
    }
});

and i can add to that when i need to handle a filter or sorting. my back-end handles all the responses, so it made things super simple.


#4

I was pretty surprised myself to find that there really is no standard solution for both client-side and server-side pagination in Ember. Originally, my big plan was to use http://datatables.net, but I ran into several issues whenever I needed to update an already-rendered table. DataTables wound up being a very well-written jQuery plugin that’s doing DOM manipulation, but without coordinating with Ember.

So I opted to roll my own. It actually came out really nicely, and it’s trivially easy for me to add server-side or client-side pagination (I can choose by selecting the right Mixin) on any page. If I had more free time, I would dress it up and release as open source, but in the meantime here are some tips that may help you out. Feel free to message me or post here with any specific questions.

Structure
I decided to create three Mixins for my paginator:

  • PaginatorBaseMixin
  • PaginatorClientSideMixin
  • PaginatorServerSideMixin

The baseMixin defines a common set of properties and computed properties that both the client-side and server-side paginator will need. These include: searchString, sortBy, isSortAscending, totalItems, itemsPerPage, etc. The computed properties were things like nextPage, prevPage, itemLowerBound, itemUpperBound, and availablePages.

That last one, availablePages is responsible for deciding which pages at the bottom of the screen to render, like 1…4, 5, 6, 7 … 19. That turned out to be kind of a funny algorithm but it works nice now.

In the individual serverSideMixin or clientSideMixin, the main differences are how nextPageClick is handled (do we reload from server, or simply update the currentPage property), and how to get the content itself.

In the serverSideMixin, as you discovered, query_params are an ideal fit here. Ultimately, each time you sort a column or advance a page, or really do anything, thanks to query params, Ember re-queries the server and gets fresh data.

To answer your question about local storage for serverSidePaginator, the key issue here is that Ember is only getting a subset of your full data. Let’s say your actual data set has 1,000,000 rows. Because Ember doesn’t have the full data set, it can’t handle any sorting locally. And even page advancing should trigger the served b/c maybe there’s new data since we last checked?

For the ClientSidePaginator, I just download all the content upfront in my model: handler in the route, and use a computed property that wraps this that does nothing more than slice the content from index 1 to index 10, or whatever page you’re on.

I know this was a bit of a ramble, but hopefully has some helpful tidbits.


Integrating DataTables into a simple EmberJS application
#5

These are some great ideas, thanks for sharing! Indeed, I will need to think about how to make my solution more modular in the near future- mixins it seems is the way to go. Fortunately for now i only need to worry about a few tables, but once I get more comfortable with how it works, I will see about bundling it up a little tighter.