In this tutorial, I’ll be showing you how to add a simple pagination to your AngularJS WordPress theme. We’ll create a custom directive with AngularJS which works like the WordPress built-in function posts_nav_link()
, that will display previous and next posts links in your index and category pages.
Before we get started, let’s take a look at the posts_nav_link()
function first. By its description from the WordPress Codex, posts_nav_link()
‘displays links for next and previous pages. Useful for providing “paged” navigation of index, category and archive pages.’ So after we add our custom directive “postsNavLink
” to our AngularJS app, we expect to see a paged navigation in our index and category pages:
You might notice that posts_nav_link()
takes three parameters:
$sep
: Text displayed between the links.$prelabel
: Link text for the previous page.$nxtlabel
: Link text for the next page.
Since we’re inspired by posts_nav_link()
to build postsNavLink
directive, we’d like to take the same parameters as attributes. We can use our custom directive as simple as:
<posts-nav-link></posts-nav-link>
Or we can pass values into the attributes to make some changes:
<posts-nav-link sep="//" prev-label="« Previous Page" next-label="Next Page »"></posts-nav-link>
Looks cool, right? If you’re ready, let’s dive into the code!
O. Getting Started
Please get the theme files from the GitHub repo. It would be easier to follow the steps in this tutorial with this ready made theme. After downloading the theme, you must change the “base href
” at line 4 in index.php.
For example, if you host your site at the root of the domain, you’ll need to change it to “/”; if you host your site in the “jsonapi” directory of the domain, which your website URL will be like “http://localhost/jsonapi”, you’ll need to set the base href
to “/jsonapi/”.
And don’t forget to install and activate the WP REST API plugin.
To make the demonstration simple, I’ll set the blog to show only one post per page at the WordPress Administration “Reading Settings” Screen.
1. Setting up the routes
First step we’ll do is to set up the routes to our “paged” pages. At line 20 – 27, we add two new routes for the paged category and index page. As you can see, we set the route paths consist with the default WordPress URL structure. For example, the URL of page 2 for index page would be [site-url]/page/2/
, and the URL of page 3 for uncategorized posts would be [site-url]/category/uncategorized/page/3/
.
If you haven’t do so (I’ve mentioned the permalink settings in my previous tutorial), you need to set the permalink structure in WordPress as the following screenshot to get the best SEO performance. That when search engines crawl your website, they won’t run into a bunch of 404 pages or pages without correct content.
2. Updating the Main controller
When we’re trying to add paged navigation to our AngularJS app, the most importance thing is to know what are the values of “current page” and “total pages“. Luckily WP REST API has passed them to us in headers.
We’ll need to add the headers
parameter to our function (at line 9) to access it. At line 14 and 15, we also add two new properties: currentPage
and totalPages
to the $scope
object. We set currentPage
to 1
because we’re at homepage, and we get totalPages
from headers
which is a function passed by WP REST API, so we get the value with syntax like headers("X-WP-TotalPages")
.
We’ll use the values from these two properties to help our postsNavLink
directive to show or hide the pagination links.
3. Creating the postsNavLink
directive to display the previous / next posts links
If you’re not so familiar with custom directives in AngularJS, I’ve written a tutorial for you: Using AngularJS Custom Directive In A WordPress Theme. I’d like to analogize the custom directives in AngularJS to the template tags in WordPress, that we wrap some reusable code snippets into a function, and call it from our theme when we need it.
In this case, we need a function to display the previous / next posts links in our AngularJS app. Basically it would be exactly like an AngularJS version of posts_nav_link()
. Let’s see how to create the postsNavLink
directive:
- Line 7: we’ll create a new template file for this custom directive. I named it
posts-nav-link.html
but you can choose any file name you like. - Line 9: we’ll get the current page number from the route parameter “
page
“. - Line 10: the linkPrefix can help us to set the different route paths for index and category page.
- Line 12-18: we create a new property for
$scope
object calledpostsNavLink
, which is an object to contain all information our custom directive will use.
Let’s take a look at the controller in our postsNavLink
directive, we just pass a new parameter $element
into it. The $element
object represents the directive itself, and it’s a jqLite element. Since jqLite is a lite version of jQuery, we can manipulate it with some of the jQuery methods.
For example, we use $element.attr('sep')
to get the value of the sep
attribute in the postsNavLink
directive.
4. Creating the pagination template posts-nav-link.html
Let’s create a new file posts-nav-link.html
in the partials
directory. In the template file we add all required HTML there with some AngularJS attributes and markups:
- Line 2: We get the
preLink
andprevLabel
frompostsNavLink
object. And we useng-if
with the condition that only show the previous link whencurrentPage>1
. - Line 3: The
sep
text between two links should be displayed whencurrentPage>1 && currentPage<totalPages
. - Line 4: We get the
nextLink
andnextLabel
frompostsNavLink
object. And we useng-if
with the condition that only show the next link whencurrentPage<totalPages
.
5. Creating the Paged controller
When we set the new route for paged index page, we added a new “Paged
” controller. Now let’s create it.
Basically the Paged
controller is pretty much like the Main
controller, we just make some modification:
- Line 4: We add
$routeParams
to the controller function, that for us to get thepage
parameter from the route. - Line 9: We query paged posts with endpoint like
[site-url]/wp-json/posts?page=3
. - Line 10-12: Set
currentPage
andtotalPages
properties. Please note we useparseInt
to convert$routeParam.page
to be an integer, or it would be a string and can’t be used to compare with integers. - Line 15-16: Update the page and document title.
6. Updating the Category controller
Let’s take a look at the Category
controller. The Category controller looks a bit messy at this time, and so do other controllers. I’ll write another tutorials about how to use AngularJS service
to tidy up our code later.
For now let’s just focus on line 15 – 24, where we add pagination to category pages by changing the endpoint to query posts with category_name
and page
. And we also add currentPage
and totalPages
to $scope
object. These are important properties we’ll use in our directive, so we must make sure they’ve been attached to the $scope
.
7. Updating the searchForm directive
After the steps above, we’ve almost got everything done. But there might be a feature we have broken – the search function.
Because we add pagination to the index and category pages, the search results will be paginated either. For technically the search results page is also one of the archive pages in WordPress. But when I built the searchForm
directive in previous tutorial, I made it very simple to only demonstrate the search function, and I didn’t make it a standalone page so it didn’t get its own route.
Here I still want to keep it simple so the quick and dirty method is to add “posts_per_page
” to filter and set it to “-1
“. I also set the currentPage
and totalPages
to “1
” because I don’t want to paginate the search results. (at line 13 – 18)
8. Add the postsNavLink
directive to main.html
Now it’s the final step! Just add the postsNavLink
directive to the bottom of the main.html
. Remember you can set three attributes value: sep
, prev-label
and next-label
. (I set prev-label
and next-label
in the following example.)
After these steps we finally add pagination links to index and category pages in our AngularJS WordPress theme. Here’s the screencast to show how it works in category pages:
You can get the final theme files from the project repo on GitHub. I hope you enjoy this tutorial as much as I do. If you have any problem when following the instructions, just send me an email. I’d love to get back to you as soon as I can. Talk to you soon!
Hi Yoren Chang,
Your posts are so great. I’m a new babie so please advise me in case I set up searching posts in WordPress with AngularJS and the data is about 2-3 million rows (posts- terms).
How to make it fast? Tks much for any advise. Hai
Hey Hai,
I have no experience on dealing with such significant amount of data in WordPress, but I believe ElasticSearch is what you’re looking for.
You can check out ElasticPress by 10up: https://github.com/10up/ElasticPress. I haven’t test it but since it integrates with WP_Query, it should work with WP API.
Cheers!
Hello, thank you for this great tutorial,
I tried your code in my project, the search results give the match post first and after a tiny delay it adds all the posts.
snipped screen
Strange, it works with my demo, as the gif showed in my post.
I’ll add a live demo site soon so people can be sure that it does work like my tutorials.
I couldn’t see any pagination at first – currentPage always = totalPages (1)
so I added the extra ‘posts_per_page’ filter parameter to the Category controller to get it to work.
i.e. changed
var request = 'wp-json/posts/?filter[category_name]=' + res[0].name;
to
var request = 'wp-json/posts/?filter[category_name]=' + results[0].name + '&filter[posts_per_page]=2';
In this example 2 posts are shown per page and you’d get a previous/next link in the pager (when there are more than 2 results)
hey in WP-API the posts_per_page goes with the same value in WordPress settings. So the default value is “10”. I actually changed the default value to “1” to get this demo work.
No worries Yoren 🙂 I figured it was something like that.
I can see from your gif that only one was appearing, but in the code example on this page I can’t see where you’ve set it.
…and thank you for a clear tutorial 🙂
Glad I can help! 😉
I think that your comment is good for custom post types….with small fixes….
Answer about THAT_BMAN_AGAIN comment
Hi Yoren,
Thanks for this tutorial,
I am having issues with the headers, this is my console message:
ReferenceError: headers is not defined
at scripts.js:50 ($scope.totalPages = headers(‘X-WP-TotalPages’);
)
at angular.min.js:87
at angular.min.js:119
at n.$eval (angular.min.js:133)
at n.$digest (angular.min.js:130)
at n.$apply (angular.min.js:133)
at h (angular.min.js:87)
at K (angular.min.js:91)
at XMLHttpRequest.z.onload (angular.min.js:93)
Hi Yoren,
All good I found the issue was a syntax error! Crisis averted!
thanks
Hey, glad you figured that out. 🙂
Hi. Could you please explain what was the error? I have the same problem, but could not find the solution.
Thanks in advance.
Hello Fernando,
You need to take “headers” as the third params to your success function at https://gist.github.com/yoren/5114bc80065e566e2f30#file-scripts-main-controller-js-L9.
It worked! Thanks for the answer! =]
I am still following the tutorial posts for a brand new project and I am enjoying a lot.
Hi,
about the posts-nav-link.html, just a correction. The links for previous and next should be the oposite:
<a data-ng-if="currentPage{{postsNavLink.prevLabel}}
1 && currentPage{{postsNavLink.sep}}
1″ href=”{{postsNavLink.nextLink}}” class=”next-link”>{{postsNavLink.nextLabel}}
Hi, is it possible to set a pagination (just prev and next) also in the content? to show the previous/next post in the same category? I would appreciate if you can help me out on this
Thanks in advance
Luca
Ah I think you need a custom endpoint to do that. Try to create one and feel free to come back when you need my help to check your code.
It would be cool when using a ” wp-json/wp/v2/posts?slug= ” endpoint if the next and previous post slug were returned together with the post data. I’ll try to work on this! Thanks for your guide Yoren, I’m trying to rework my wordpress site using your angular js theme.
I found this issue https://github.com/WP-API/WP-API/issues/783 not sure if it’s the right way to do this.
Hey Luca, Ah I think you’re in the right direction! Have you tried this solution: https://github.com/WP-API/WP-API/issues/783#issuecomment-94033307?
And please note you might need to update the filter name to “rest_prepare_post” if you’re using WP API v2.
Hi Yoren, I tryed to inject the previous and next in my json for each posts, but the code seems not working, I changed correctly the filter as your thumbnail image injection.. 🙁
Well, I’ve fixed with this Plugin: https://github.com/akmur/add-prev-next-to-wp-api it’s works like a charm 🙂
Hi Luca, Sorry for my late reply and good job for digging such awesome plugin!