In this post we’re going to take a look at how to page data in Phoenix with scrivener_ecto.
These days infinite scrolling is a popular way to achieve paging but isn’t always appropriate for every situation. Infinite scrolling works great for a news feed or user posts, but not so much for something like a product list. So we’ll be looking at how we can go about implementing traditional style paging.
What we’ll build
To demonstrate paging we’re going to build a simple list of products.
So let’s get started!
Create the app
We’ll start from scratch with a new Phoenix application and add some simple scaffolding for the purposes of demonstrating paging.
Y when asked to fetch and install dependencies. Now we’ll change into the application directory and create the database.
Ok, our basic application is good to go, now let’s add some simple scaffolding.
Adding some scaffolding
As per the
mix output, let’s update the routes…
/lib/paging_web/router.ex …line 16
… and run the migrations.
And with that our initial application is complete, but we don’t have any data to page. We could enter some data manually; instead let’s use a seed file.
Seeding some data
Phoenix already creates a
seeds.exs file for us when we create a new project, so all we need to do is add the appropriate code.
Pretty simple! We only want to create our seed data when running in development so we’ve wrapped our code in
Mix.env == :dev. We then use
Enum.map to create 100 products. We use
Widget <index value> for the name of the product, and a random number between 10 and 200 for the quantity.
Now we can seed our data.
Let’s have a quick look at our application prior to adding paging.
If you navigate to http://localhost:4000/products you’ll see the products we created.
We’re now ready to move onto adding paging.
We’ll split the paging implementation into a couple of parts. First we’ll deal with the back-end code and then address the front-end.
Adding paging on the back-end
As mentionned earlier we’ll be using the
scrivener_ecto package to accomplish our paging, so let’s start by adding the package.
/mix.exs …line 34
scrivener_ecto to our
mix.exs file and now need to get our new dependency.
We’ll see that both the
scrivener_ecto packages are added.
Next we need to update our
Repo module so that we can make use of the paging functionality. We do this by adding a
use Scrivener statement in
repo.ex. This makes the
scrivener_ecto functions available thru the
We’ve also set a default page size of 10 via the
To make use of the paging functionality in our products page we will update the
list_products context method. It needs to take in a parameter which we’ll simply call
params; and it also needs to call into the
paginate function instead of the
all function, i.e.
Now we need to add a parameter to the controller method as well which we pass thru to the
list_products context method.
And that’s it! Let’s restart the server.
And we can test out our paging in the browser; we see by default the page size is 10 items.
By changing the URL we can control both the currently displayed page and the page size via the
page_size parameters. For instance http://localhost:4000/products?page=5&page_size=3 yields:
We probably don’t want to make our users update the
URL manually when they want to switch pages thou! So let’s add some controls in the UI.
Adding paging on the front-end
We’ll take two approaches to adding some UI controls on the front-end. First we’ll take a custom approach and directly add
Next buttons. Then we’ll look at a package that provides us with an easy way to create a standard pagination navigation bar.
Adding paging buttons
The first step with either approach is to update our controller method to push the current
page parameter to our template.
Above we’ve renamed the variable returned from
page as this is what is being returned, a single page of products. We then assign
page.entries to the
products template variable and pass the
page parameter to the template.
We then need to make some minor changes to the template.
We’ve added two buttons for our paging controls, along with some simple logic to disable the
Previous button when we are on the first page; and the
Next button when we are on the last.
We now have the ability to change our page without having to manually update the URL!
This works pretty good, but let’s have a look at an alternative approach via the scrivener_html package.
Adding paging buttons via the scrivener_html package
Another option for adding UI paging elements is to use the
The first step is to install the package
/mix.exs …line 34
scrivener_html added to our application, we now need to add a configuration entry for the package.
/config/config.exs …line 25
We’ll add the configuration right after the
JSON configuration. There are a number of view style options availble that correspond to different CSS frameworks. There isn’t one for Milligram (the default CSS framework for Phoenix) so we’ll just stick with the Bootstrap view style.
Next we need to import
Scrivener.HTML in any views in which we want to make use of the package. So we’ll update
Now we just need to update our template. We’ll keep our existing paging buttons in the template as a comparison, but you’d obviously want to use a single paging control if building out a real application!
All we need for the pagination links is a single line:
<%= pagination_links @page %>… sweet!
Now with a server restart we’ll see some new paging links show up.
The links work, but they are not looking so great! We can add some basic styles to make them look a little better.
We’ve added some pretty simple styling, you might want to add hover effects and other elements in a real implementation, but at least things are horizontal now and the active page is highlighted.
If using one of the many CSS frameworks
scrivener_html supports you’d get attractive paging links styled as per your chosen framework, i.e. with
Bootstrap installed you’d see.
So with the help of the
scrivener_ecto package we can see that paging in Phoenix is a snap! And the
scrivener_html package makes it easy to add paging navigation to our UI, heck, what more could you ask for?
Thanks for reading and I hope you enjoyed the post!