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

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:

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:

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 thoughts on “Category Pages In AngularJS WordPress Theme”

  1. 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’;
    });

    Reply
  2. 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.

    Reply
  3. 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?

    Reply
  4. Yoren,

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

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

    Reply
  5. 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.

    Reply
    • 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.

      Reply
      • 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.

        Reply
        • 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.

          Reply
  6. 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.

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

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

    Reply
      • 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;

        Reply
  9. 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.

    Reply
    • 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.

      Reply

Leave a Comment