Straightforward Pagination With Adonis JS

This article is for beginners, so if you already know what you are doing in Adonis JS, you may read on if you want and be kind enough to share it with some beginner.
In almost every application you build, you will need to render data to some page for the user to view or interact with, while these data may be just a picture, a few numbers, or a small string of text; some other times they are large amounts of data sets that it will not make sense to include all that data in one page. You just have to paginate it.
If you are new to Adonis JS and especially if you are coming from the Laravel world, you would expect to be able to add pagination to your view with just one method in Adonis JS like you can do in Laravel, I assure you however that this is not the case; you will need to do some work, howbeit not a tedious one.
Let’s say we have a blog that has a hundred articles that we want our users to be able to read. Since loading one hundred articles at once may slow down our application, we will need to paginate it and give the user access to a small number of those articles on demand.
Create a new Adonis project:
adonis new pagination-tutorial
Change directory to your project
cd pagination-tutorial
Let’s set up our database configuration in the .env file. We will be using MySQL for this tutorial.
HOST=127.0.0.1
PORT=3333
NODE_ENV=development
APP_URL=http://${HOST}:${PORT}
CACHE_VIEWS=false
APP_KEY=******************
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASSWORD=
DB_DATABASE=pagination-tutorial
SESSION_DRIVER=cookie
HASH_DRIVER=bcrypt
Remember to create a database in MySQL with the corresponding database name; and install the MySQL driver to help us interact with the database.
npm i mysql
Ok, now that our database is set up, we need to set up our model and migration for the articles.
adonis make:model Article -m
The command above will create an Article.js
file in the app/Models
directory of our application and also create a migration file which you can find in the database/migrations
directory.
You can use the model to do so many things, one of them is to establish relationships between tables; feel free to drop a request in the comment section if you would like me to be to write more about that. Now let us define our database schema:
class ArticleSchema extends Schema {
up () {
this.create('articles', (table) => {
table.increments()
table.string('article_title')
table.text('article_body')
table.string('article_image')
table.timestamps()
})
}
down () {
this.drop('articles')
}
}
You may already know what the table.string()
method does but not the table.text()
method; the table.text()
method allows us to store much longer strings as opposed to 255 characters which the VARCHAR(table.string()) will allow. Now we can run our migration:
So we have our database schema setup and we can now run the command to set up our table in the database.
adonis migration:run
In an ideal situation, you would have data which either you or other authors have written, this data would normally be posted through a form to your backend and saved in your database but in this case, it will be too slow for us to post one hundred articles manually so we will be seeding the data.
Database seeding is a process of initializing an application with data similar to what we will be using in production so we can build faster. If you would like me to write about seeding data with Adonis JS please leave a comment requesting it and I will take it up.
We can create a seed file which will be found in the database/seeds/
directory using the following command:
adonis make:seed Article
In the database
directory you have a factory.js
file. We will use the factory file to define the blueprint for the data which we would like to seed into our database.
Factory.blueprint("App/Models/Article", (faker) => {
return {
article_title: faker.sentence({ words: 6 }),
article_body: faker.paragraph(),
article_image: faker.avatar({ fileExtension: "jpg" }),
};
});
Next, we go back to our ArticleSeeder.js
file to define how many articles will be created, we want to generate one hundred articles.
const Factory = use("Factory");
class ArticleSeeder {
async run() {
await Factory.model("App/Models/Article").createMany(100);
}
}
module.exports = ArticleSeeder;
Once we have defines our requirements for seeding, we can now seed our database:
adonis seed
We have come a long way now, our database has been seeded and all we need to do is to fetch the data to our view and paginate it. However, to do that we need a controller, so let’s generate one:
adonis make:controller Article
This command will create an article controller in app/Controllers/Http/
directory. Now, let's fetch our data and pass it to the view:
"use strict";
const Article = use("App/Models/Article");
class ArticleController {
async getAllArticles({ params: { page }, response, view }) {
try {
const pageNumber = page || 1;
const articles = await Article.query()
.orderBy("id", "desc")
.paginate(pageNumber, 10);
return articles;
return view.render("welcome", {
articles: articles.toJSON(),
});
} catch (error) {
console.log(error);
session.flash({
notification: {
message: "Failed to fetch all articles",
},
});
return response.redirect("back");
}
}
}
module.exports = ArticleController;
To understand what we are doing in the controller above, first, we need to understand the data that is being returned. Adonis returns an object that tells us the total number of items being sent, the number of items per page, the current page, the last page and an array of our actual data.
Please note that the object below has been adjusted so we can see the details of the data that we are to work with.
{
"total": 100,
"perPage": 1,
"page": 1,
"lastPage": 100,
"data": [
{
"id": 100,
"article_title": "Tu keclagjew accu ucok ga olegi.",
"article_body": "Otasasik awoivu dewib sas evisogra fo ekumerav risi osoeguafo kihug femume noazzok hopsor et tag rah. Sa ipa vume kucefhu vusu fuheizi cofi fah pecir bimug na vuhehukej. Limep kel nukulus ujahofu amhu ijwuz eja ke sus ilo evudi pavalnur bikficat nasoj oreotuga ho fehumbir ga. Hezowbe tosifu tunev obnerpam tol bibbaveto cem mobjaro mikikim ulbo lojdifket cev.",
"article_image": "//www.gravatar.com/avatar/fe37dabb40782da0c5b3f4116764c4ad.jpg",
"created_at": "2020-09-25 12:37:30",
"updated_at": "2020-09-25 12:37:30"
}
]
}
So back to what we are doing in our controller. First, we get the page
from our params
object and we determine if any value is being returned otherwise we set pageNumber
to 1.
After that, we fetch the articles ordered such that we get the latest article first. We also pass the pageNumber
and the number of articles we want to receive on each page to the paginate method. After that, we just convert the data to JSON and pass it to our view.
To be able to receive the optional parameter, we need to add it to our route as shown below. The question mark after the parameter tells Adonis JS to treat the parameter as an optional parameter so it will not throw errors if there is no value present there. We have also added naming for our route .as("articles")
to make it easier to reference. You can find the routes.js
file in the start
directory.
Route.get("/:page?", "ArticleController.getAllArticles").as("articles");
Now we can go to resources/welcome.edge
(our edge template) to add our HTML. Here I am using Bootstrap cards.
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Adonis Pagination Tutorial By Elvis Onobo | elvis.onobo@gmail.com</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>
<body>
<h1 class="text-center">Page {{ articles.page }} of {{ articles.lastPage }}</h1>
<div class="col-md-12 row justify-content-center">
@each(article in articles.data)
<div class="card m-2" style="width: 18rem;">
<img src="{{ assetsUrl(article.article_image) }}" class="card-img-top" alt="{{ assetsUrl(article.article_title) }}">
<div class="card-body">
<h5 class="card-title">{{ assetsUrl(article.article_title) }}</h5>
<p class="card-text">{{ assetsUrl(article.created_at) }}</p>
<a href="#" class="btn btn-primary">Read Article</a>
</div>
</div>
@endeach
<div class="col-md-6 col-md-offset-3 row mt-2">
<a href="{{ articles.page == 1 ? '#' : route('articles', { page: articles.page - 1}) }}" rel="prev" class="btn btn-primary">Previous</a>
<p>Page {{ articles.page }} of {{ articles.lastPage }}</p>
<a href="{{ articles.lastPage == articles.page ? '#' : route('articles', { page: articles.page + 1}) }}" class="btn btn-primary">Next</a>
</div>
</div>
</body>
</html>
So what are we doing here? We are iterating over our data to show the data from our database. Note that we are accessing articles.data
and not articles
for our iteration.
Outside our @each
statement is where the magic happens. In our anchor tag, we check if articles.page
is equal to one and if it is, then we do nothing. Otherwise if articles.page
is greater than one, we subtract one from it to get the previous page we should go to; we then send this previous page to the route for the controller to use.
The exact opposite happens with the Next
button. Here, we check if articles.page
is equal to the articles.lastPage
which means we are on the last page, if the current page is the last, then do nothing, otherwise we add one to the current page and pass it to our controller through the route.
That is it, your pagination is ready!
Thank you for reading this far. If you found this article informative in any way, please hit the like button or leave a comment to keep me going. Also, I would like if you share the article with your network and anyone you think it may help.
Let’s connect on Twitter and on LinkedIn too.
You can find the code for this article on Github.
Cheers to your success!
Originally posted here: https://elvisonobo.hashnode.dev/straightforward-pagination-with-adonis-js