Category Pages In AngularJS WordPress Theme

In this tutorial I’ll be showing you how to add category archive pages to our AngularJS WordPress theme. Topics will be covered including:

  1. Get categories with WP REST API
  2. Display category links
  3. Create the route to category pages
  4. List posts in a certain category
  5. Replace the current category link with plain text to improve the usability
  6. Bonus: Display the featured image in posts list
  7. Bonus: Use JSON Formatter to improve the readability of JSON data and help you debug
  8. 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 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:

Category Pages In AngularJS WordPress Theme

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:


With such format we can also get all tags with the following endpoint:


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:


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 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:

Category 1 archive page

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:

Display featured image in posts list
Display featured image in posts list

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:

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.

Screenshot 2015-04-11 20.42.49

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.

Change category base at Permalink Settings

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.

42 responses

  1. […] 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 […]

  2. […] my learning path about building a WordPress theme with AngularJS. So far we’ve built one with category archives and pagination, and also a functional search form in it. You can just download it and focus on […]

  3. 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’;

    1. Yoren Chang Avatar
      Yoren Chang

      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:

      And now you can access {{post.featured_image_thumbnail_url}} in your AngularJS template.

      1. Thanks Yoren! That’s a much better solution and I really appreciate your help.

  4. David Avatar

    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.

    1. Yoren Chang Avatar
      Yoren Chang

      Maybe try to add posts to the term data? You can do so with json_prepare_term filter:

      1. David Avatar

        Thanks 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?

        1. Yoren Chang Avatar
          Yoren Chang

          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.

          1. David Avatar

            I’ll be patient for it! Thank you very much

        2. Yoren Chang Avatar
          Yoren Chang

          David, here’s the gist to demo how to filter the JSON data for a term:

          And with this filter, the JSON data will look like this:

          Notice that the latest 5 posts are included. The endpoint to query all terms will be changed, too.

          1. David Avatar

            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!

          2. Yoren Chang Avatar
            Yoren Chang

            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!

  5. Daniel Avatar


    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)
    $ = $filter(‘filter’)($scope.posts, { slug: $routeParams.slug })[0];

    I want to filter the post by category. Any Ideas?

  6. Daniel Avatar


    I actually got it figured out. I filtered the posts by category by using:

    $scope.posts = $filter(‘filter’)(res, $routeParams.category);

    1. Yoren Chang Avatar
      Yoren Chang

      Hey good for you! And thanks for sharing. 😉

  7. […] David asked this question that he wanted to build an accordion menu with categories as the first level links, which when being clicked, the posts would be collapsed as the second level links. […]

  8. 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.

    1. Yoren Chang Avatar
      Yoren Chang

      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.

      1. 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 “” by manually it automatically converts like this “”.

        And my taxonomy api is not working, that is “”.

        Please help me to solve this problem.

        1. Yoren Chang Avatar
          Yoren Chang

          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.

  9. Thanks YOREN;

    Those problems have been resolved. Now it’s running well.

    thank you very much for helping me out.

    1. Yoren Chang Avatar
      Yoren Chang

      Glad you figured them out! You’re welcome.

  10. 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
    After that if i click any menu its not working properly.

    I there is some routing problem.

    app.config([‘$routeProvider’, ‘$locationProvider’, function ($routeProvider, $locationProvider) {
    // 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”});
    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.

    1. Yoren Chang Avatar
      Yoren Chang

      From my perspective, it seems worked now. It’s good to know you can always sort things out. Cheers.

  11. Hey YOREN,

    Thanks for this blog. it was so helpful for me.

  12. 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?

    1. Yoren Chang Avatar
      Yoren Chang

      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:

  13. 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

    1. Yoren Chang Avatar
      Yoren Chang

      hey Em, you actually can pass slug to category_name filter, so the white space won’t be a problem. Cheers!

      1. 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]=’ +;
        in to
        var request = ‘wp-json/posts/?filter[slug]=’ +;

        1. Yoren Chang Avatar
          Yoren Chang

          hi Em, it should be:

          var request = ‘wp-json/posts/?filter[category_name]=’ + category.slug;

  14. Yoren thank you for your swift and precise reply!

  15. 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
    This is not working for me. Posts are not showing. This is what I get –

    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

    1. Yoren Chang Avatar
      Yoren Chang

      Hi Rohan,

      What if you visit the category route directly? Like what I did in this screenshot:

      And is there any error in your JS console?

      1. 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?

        1. Yoren Chang Avatar
          Yoren Chang

          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?

  16. 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.

    1. Yoren Chang Avatar
      Yoren Chang

      Hey Joe, you can use the same function but change the filter from rest_prepare_post to rest_prepare_job if the CPT is “job”. The filter is actually in this format: 'rest_prepare_' + CPT.

  17. I was wondering how to display my list of categories in WP API 2. Having issues with nothing showing.

    1. Yoren Chang Avatar
      Yoren Chang

      Hi, Mark,

      Can you check my later post about upgrading WP API v1 to v2:

  18. this used to get data from parent category. but how to get sub category from parent category in AJ.

Leave a Reply

Your email address will not be published. Required fields are marked *