In this tutorial I’ll be showing you how to add category archive pages to our AngularJS WordPress theme. Topics will be covered including:
- Get categories with WP REST API
- Display category links
- Create the route to category pages
- List posts in a certain category
- Replace the current category link with plain text to improve the usability
- Bonus: Display the featured image in posts list
- Bonus: Use JSON Formatter to improve the readability of JSON data and help you debug
- Updated 04/11: Get category archive page with slug
Before we get started, I hope you’ve read some tutorials from my Building themes with AngularJS and JSON REST API series, so we can be on the same page later when we dive into the code. If you’re new here, I’ll recommend that you start from the first post in this series: Using AngularJS and JSON API in Your WordPress Theme, which is listed on the Resources page at wp-api.org. It will definitely help you to build a quick and dirty AngularJS WordPress theme, so please go check it out.
When I was talking about adding category archive pages to our project, I meant you’ll make the theme work like this:

I know it didn’t look too impressive but now you get the idea. You can grab a copy of my AngularJS demo theme and let’s write the first line of code to get categories with WP REST API.
1. Getting categories with WP REST API
To get all categories from your WordPress site with WP REST API, we’ll modify our Main controller to the following code:
In line 7-9, you can see the endpoint looked like this:
wp-json/taxonomies/category/terms
With such format we can also get all tags with the following endpoint:
wp-json/taxonomies/post_tag/terms
And you might notice that in line 13 I add a new property to $scope
object called “pageTitle
“, which I’ll use to make the pages more distinguishable later when we switch from homepage to a category page.
2. Display category links
Since we’ve added the categories
property to the $scope
object, now we can use it in our main.html
.
We can loop the categories just as we looped the posts – with the lovely ng-repeat
. In line 5 I set the url href to the category page in such format:
category/{{category.ID}}
So in next step, we’ll need to create a corresponding route to make it work. And just a quick note, I’ve added the {{pageTitle}}
in line 8 to display a simple page title here.
3. Create the route to category pages
At this point, you should be familiar with AngularJS route that every time when we create a new view (a new page), there must be a corresponding route. By doing so, AngularJS can get the right view for us when the route (URL) changes.
In line 16-19 is the new route I created for category pages. Please note the routeParam :category
is the category ID.
In this route I used the same template file as the homepage, but created a new controller called Category
. In the Category
controller we’ll get posts in the category, and we’ll add a new property to the $scope
object to identify the current category.
4. List posts in a certain category
You can see in the Category
controller, I kept the same snippet to get all categories in the Main
controller, so I can still list all categories in a category page.
In line 8, I used the endpoint in WP REST API to get a certain category object by it’s ID:
'wp-json/taxonomies/category/terms/' + $routeParams.category
The only reason I did this is to get the name of the category, which is res.name
that I put in pageTitle
and document title. I even used it to get posts belong to it.
In line 13, you can see the endpoint in WP REST API to get posts from a certain category is wp-json/posts/?filter[category_name]=category_name
.
Now I’ve passed everything the view (main.html
) needs, it’s time to improve the view.
P.S. In line 9 I added a new property to $scope
called current_category_id
, which would be very useful in next step.
5. Replace the current category link with plain text to improve the usability
Now you can test to see if the category route and controller work. When clicking “Category 1” link, you should see the Category 1 archive page like this:
In such view the best practice regards to the usability, is that we should remove the link of the current category, so our users can be less confused (even though a experienced user would tell from the color of the links).
To replace the current category link with plain text (only category name), I’ll introduce a new built-in directive in AngularJS called ng-if
.
It’s obvious that ng-if
can be used to help us hide or show something in certain conditions. Check the line 6-7 to see how I set the conditions that show or hide category links. Hint: the current_category_id
played a very important role.
6. Bonus: Display the featured image in posts list
Since we talked about ng-if
, another example to use it is to display the featured image.
With ng-if
the featured image only shows when it’s available, like the following screenshot:

In line 5 I’ve also introduced another new built-in directive called ng-src
, which is very helpful when the image path contains AngularJS markup like {{path}}. Because browsers will fetch from the URL with the literal text {{path}} (rather than the rendered value) if we put the {{path}} to src
attribute.
7. Bonus: Use JSON Formatter to improve the readability of JSON data and help you debug
Whenever I needed to test if an endpoint in WP REST API works, I always entered the URL in browser and then viewed the JSON data.
After a while I felt really frustrated that the JSON data always looked messy and didn’t really help debugging. If you have the same feelings, I believe the JSON Formatter extension (it’s for Chrome, but you must can find similar extensions for Firefox) is all what we need.
The JSON data from the WP REST API will look gorgeous after we install the JSON Formatter:
8. Updated 04/11: Get category archive page with slug
Starting from WP REST API 1.2, we can get terms with slug by passing the filter
argument. For example, we can call the API with /wp-json/taxonomies/category/terms/?filter[slug]=uncategorized
to get the uncategorized category, which would be an array contains the term object.
For this matter now we can get category archive page with slug. Let’s update our scripts to do so.
First, we’ll change the endpoint to get the category (at line 9). And because now we get the result as an array, we need to update all res
object to res[0]
.
Then we update the link href in main.html
, we change the link from “category/{{category.ID}}” to “category/{{category.slug}}
“. And everything will work just as we expected.
Last but not least is to change the “Category base” to “category” at the Permalink Settings page. By doing so we can make the default permalink to category archives in WordPress to be “[site-url]/category/%category%/”, rather than “[site-url]/blog/category/%category%/”, and it will consist with the category route path in our AngularJS app.
Whenever we add new routes in our AngularJS app, we’d better to make sure that the route paths consist with the permalink structures in WordPress. Because search engines are good at following the default URL structure generated by WordPress, and if they visit a URL doesn’t match any route paths in our AngularJS app, they will be redirect to the default route path (homepage in our case). If there are too many pages are redirected to homepage, you’ll get into trouble on SEO performance.
Now you can get the whole project files from the Github repo. I hope you enjoy this tutorial and learn something valuable. If you still get confused by some AngularJS terms or concept, be sure to check out the post Minification Errors in My AngularJS WordPress Theme, that I kind of summed up the first 3 tutorials in my AngularJS series to help readers to get a clear picture.
If you bump into any issue related to this tutorial, please don’t hesitate to contact me. I’d love to get back to you as soon as I can.
Great series of posts! Yesterday I finished the most recent one (WPService) and got a bit adventurous afterwards. I started looking into v2 of the WP API. Version 2.0-beta1.1 to be specific. Most of the differences were simple namespace changes however I’m stuck on one larger change to the API. The “/posts” endpoint no longer contains the url to the featured_image. The only way it seems to get the featured image now is to query the “/media” endpoint with the “featured_image” id that the /posts endpoint now returns. I created a foreach loop to get the media for each post but can’t get it working. Can you provide any insight into how I can get this working? Doesn’t seem very optimized but it’s the only way I can see to get the featured _image url.
$http.get(‘wp-json/wp/v2/posts/’).success(function(result, status, headers) {
$scope.currentPage = 1;
$scope.totalPages = headers(‘X-WP-TotalPages’);
angular.forEach(result, function(value, key) {
if ( value.featured_image != 0 )
{
$http.get(‘wp-json/wp/v2/media/’ + value.featured_image).success(function(result) {
// $scope.featuredThumbnail = result.media_details.sizes.thumbnail.source_url;
$scope.featuredThumbnail = result.media_details.sizes.thumbnail.source_url;
});
}
});
$scope.posts = result;
$scope.pageTitle = ‘Latest Posts:’;
document.querySelector(‘title’).innerHTML = ‘Home | AngularJS Demo Theme’;
});
Hey Dominic,
I came up with an idea that you can add the featured image URL to your JSON data if you’d like to access it with posts endpoint, so this is it: https://gist.github.com/yoren/c8c98dc7c701bb87a844
And now you can access {{post.featured_image_thumbnail_url}} in your AngularJS template.
Thanks Yoren! That’s a much better solution and I really appreciate your help.
Hi Yoren,
Any idea on how to get the categories and their posts in a single request?
This will be used to create an accordion, where once you click on the category name it will open and reveal all posts associated with it.
Maybe try to add posts to the term data? You can do so with
json_prepare_term
filter: https://github.com/WP-API/WP-API/blob/master/lib/class-wp-json-taxonomies.php#L369Thanks for the reply. Sorry to bother you further but how do I extend the wp-api with this and do you think it will include custom post types?
Hey David, I’d love to write a simple gist for you to demo this on this weekend. I hope it’s not urgent for you. I’ll post the gist here and also shoot you an email. Talk soon.
I’ll be patient for it! Thank you very much
David, here’s the gist to demo how to filter the JSON data for a term:
https://gist.github.com/yoren/95cd7779de9cc6e4c189#file-functions-php
And with this filter, the JSON data will look like this:
https://www.dropbox.com/s/5dxsdare0xoveqo/Screenshot%202015-06-05%2016.58.05.png?dl=0
Notice that the latest 5 posts are included. The endpoint to query all terms will be changed, too.
Works beautifully! It’s also quite easy to add my custom posts along with slug info.
It really seems quite easy to extend wp-api. I’ll probably have to have a long sit down with the documentation.
Thank you so much for your time once again!
David, Yep the WP API is very easy to extend, I really love the way it works. And thanks for all the contributors, 2.0 will be even better!
Yoren,
So I have a service that makes one HTTP call to get all posts. If i wanted to filter the response by category. So If I clicked on a category I get all the posts associated with that category. I know in your tutorial you have the filter in the URL. Basically I wanted to filter the json using angularjs filter. Have you done this at all?
Like this:
(this is how I get the single post in the HTTP response using the WP API)
$scope.post = $filter(‘filter’)($scope.posts, { slug: $routeParams.slug })[0];
I want to filter the post by category. Any Ideas?
Yoren,
I actually got it figured out. I filtered the posts by category by using:
$scope.posts = $filter(‘filter’)(res, $routeParams.category);
Hey good for you! And thanks for sharing. 😉
when i use statement to remove # from url it is showing error “$rootScope is not defined”.
the statement is : $locationProvider.html5Mode(true); in app.config() in my js file.
if i remove, ” $locationProvider.html5Mode(true);”. it’s working well.
please tell me how to solve this.
Hello, the best shot is to make sure you’ve set the right “base href”. If you’ve used my lastest project files from GitHub, it should be set automatically with PHP. If not, there’s a high chance the error was caused by it.
Thanks YOREN;
Actually, that problem with my sub domen. Now It works. But another problem is occurring that when i am giving the path in url “http://angular.orangemantra.co.in/portfolio” by manually it automatically converts like this “http://angular.orangemantra.co.in/verticals/portfolio”.
And my taxonomy api is not working, that is “http://angular.orangemantra.co.in/api/Taxonomy/get_taxonomy_index/?taxonomy=project-category”.
Please help me to solve this problem.
Surya, Does the taxonomy api come from WP API? I have no idea about it.
As to the redirection issue, please check the routes in your scripts.js and the permalink settings in WordPress. These settings are highly possible to cause the redirection.
Thanks YOREN;
Those problems have been resolved. Now it’s running well.
thank you very much for helping me out.
Glad you figured them out! You’re welcome.
Hey YOREN,
I face some problem about routing, i.e. when i click on the menu(services) it goes on correct url but when i reload the same page it sends me other url like http://angular.orangemantra.co.in/verticals/services.
After that if i click any menu its not working properly.
I there is some routing problem.
app.config([‘$routeProvider’, ‘$locationProvider’, function ($routeProvider, $locationProvider) {
$routeProvider
// Home
.when(“/”, {templateUrl: myLocalized.partials + ‘home.html’, controller: “GetHome”})
// Pages
.when(‘/verticals/:orderId’, {templateUrl: myLocalized.partials + ‘verticles.html’, controller: ‘GetVerticles’})
.when(‘/services’, {templateUrl: myLocalized.partials + ‘service.html’, controller: ”})
.when(‘/about-us’, {templateUrl: myLocalized.partials + ‘about.html’, controller: ‘GetAboutCtrl’})
.when(‘/verticals’, {templateUrl: myLocalized.partials + ‘verticles.html’, controller: ‘GetVerticles’})
.when(“/contact”, {templateUrl: myLocalized.partials + ‘contact.html’, controller: “GetContactCtrl”})
.when(“/contact-us”, {templateUrl: myLocalized.partials + ‘contact.html’, controller: “GetContactCtrl”})
.when(“/portfolio”, {templateUrl: myLocalized.partials + “portfolio.html”, controller: “PortfolioCtrl”})
.when(‘/work/:orderId’, {templateUrl: myLocalized.partials + ‘work.html’, controller: ‘GetWork’})
.when(‘/:orderId’, {templateUrl: myLocalized.partials + ‘page.html’, controller: ‘GetPage’})
.when(‘/services/:orderId’, {templateUrl: myLocalized.partials + ‘services.html’, controller: ‘GetSevices’})
// Blog
// else 404
.otherwise(“/404”, {templateUrl: myLocalized.partials + “404.html”, controller: “PageCtrl”});
$locationProvider.html5Mode(true);
}]);
Above code has been used.
what is the problem with is?
while the same thing is running on google chrom browser very well.
Please help me for this.
From my perspective, it seems worked now. It’s good to know you can always sort things out. Cheers.
Hey YOREN,
Thanks for this blog. it was so helpful for me.
Hi Yoren,
Really informative, thanks a lot! I have a quick question…
Let’s say we are using this to build a nav bar. We want to show only the categories with no parents at first. And on hover, we can display the child categories.
Is there a way to use this and query only categories without parents? So we can display the root category? And have an additional query to see if a category has parents?
Hi Stuart, in such case you need to create a certain route/endpoint in WP API to get categories with no parents. Or you can simply modify the default route (taxonomies/category/terms) to achieve this.
For the second approach, you just need to hook to
json_prepare_term
filter. Please refer to my post for more info: https://1fix.io/blog/2015/06/26/adding-fields-wp-rest-api/.Hi Yoren,
thank you for the tutorial
I noticed that if you assign to a category a name containing a space (for example: Category One) , the posts belonging to that category don`t get displayed..
I almost haven’t modified the demo theme files so far, therefore I was wondering if it could be an issue not related to my setup
Cheers
hey Em, you actually can pass slug to category_name filter, so the white space won’t be a problem. Cheers!
I tried to modify js/WPService.min.js changing the WPService.getPostsInCategory function as follow, but it didn’t work.. thanks in advance
var request = ‘wp-json/posts/?filter[category_name]=’ + category.name;
in to
var request = ‘wp-json/posts/?filter[slug]=’ + category.name;
hi Em, it should be:
var request = ‘wp-json/posts/?filter[category_name]=’ + category.slug;
Yoren thank you for your swift and precise reply!
Hi Yoren,
Thanks so much for this tutorial. It’s helped me alot.
Regarding the display based on filter of category as per your first screenshot http://g.recordit.co/6KskGvnGPE.gif
This is not working for me. Posts are not showing. This is what I get – http://i58.tinypic.com/25yyozm.jpg
I have used exactly your code angularjs-demo-theme-slugGetCategory
-base href is fine,
-permalinks are as you have above.
Not sure what issue is,
Thanks in advance
Hi Rohan,
What if you visit the category route directly? Like what I did in this screenshot: https://onefixio-1937.kxcdn.com/wp-content/uploads/2015/03/Screenshot-2015-04-11-20.42.49.png
And is there any error in your JS console?
Hi Yoren,
Thanks for the quick reply.
I figured out the problem. In WordPress It was not picking the post category from inputting it inside of Posts > Categories but when I “Add new category” inside the post it worked. Do you know what this could be?
Hi, Rohan, this is kind weird and I haven’t seen it before. It sounds there might be something wrong with your WordPress install…
Can you switch to a default theme like twentyfifteen and see if the issue exists in a category archive page?
Thank you so much for all of your insight into how to use the WordPress Rest API. I was wondering how I could use the function for adding the featured Image URL to a custom post type? It is working for me for the default post type but it does not work for custom post types.
Hey Joe, you can use the same function but change the filter from
rest_prepare_post
torest_prepare_job
if the CPT is “job”. The filter is actually in this format:'rest_prepare_' + CPT
.I was wondering how to display my list of categories in WP API 2. Having issues with nothing showing.
Hi, Mark,
Can you check my later post about upgrading WP API v1 to v2: https://1fix.io/blog/2015/09/29/wp-api-v2-angularjs-theme/?
this used to get data from parent category. but how to get sub category from parent category in AJ.