Note added 2015/12/16: Now this post is updated to be compatible with WP API v2 Beta 9. Note added 15/12/13: With the BREAKING CHANGES in WP API v2 Beta 9, the theme is now broken even if you follow this tutorial. I'll manage to update the post soon so don't get too excited about the plugin updates.
Today in this post, I’ll be showing you how to upgrade the AngularJS WordPress theme that we have been working on for a very long time, to work with the latest WP API v2 (beta 4 at the time of this writing).
To be honest I didn’t expect the WP API v2 would be so much different from the version 1. When I talked about upgrading the theme in some comments of my tutorials, I thought it just about updating the routes and that’s all. It turned out I was wrong and the mistakes I’ve made are pretty worthy of documenting. So here we go.
You can download a copy of my AngularJS WordPress theme from the GitHub repo but it’s ok if you actually aren’t that into AngularJS – you’re just looking forward to knowing what’re the differences between WP API version 1 and 2. You can definitely learn something from this post without knowing what my AngularJS app is about, since I won’t cover too much about AngularJS today.
Changing the routes for all the AngularJS $http services
In our AngularJS WordPress theme, we use the $http
service to make HTTP requests to get the data we need. We ask WP API to send us the latest posts on homepage, all categories to display a category menu, a single post content, etc.
Here’s a full list of all WP API v1 routes we’ve used in our AngularJS app:
wp-json/taxonomies/category/terms
– to get all categories.wp-json/posts?page=PAGE
– to get posts from a certain page.wp-json/posts/?filter[s]=KEYWORD&filter[posts_per_page]=-1
– to get all search results.wp-json/posts/?filter[category_name]=CATEGORY_NAME
– to get posts in a certain category.wp-json/media?filter[post_parent]=POST_ID&filter[posts_per_page]=-1
– to get all media from a certain post.wp-json/taxonomies/category/terms/?filter[slug]=SLUG
– to get a certain category by slug.
To work with WP API v2, we need to change the routes to:
wp-json/wp/v2/terms/category
wp-json/wp/v2/categories
– to get all categories.wp-json/wp/v2/posts/?page=PAGE&filter[posts_per_page]=1
– to get posts from a certain page.wp-json/wp/v2/posts/?filter[s]=KEYWORD&filter[posts_per_page]=-1
– to get all search results.wp-json/wp/v2/posts/?filter[category_name]=CATEGORY_NAME&filter[posts_per_page]=1
– to get posts in a certain category.wp-json/wp/v2/media?filter[post_parent]=POST_ID&filter[posts_per_page]=-1
– to get all media from a certain post.wp-json/wp/v2/terms/category/?search=SLUG
wp-json/wp/v2/categories/?search=SLUG
– to get a certain category by slug.
From the list above, the most significant change that most people can spot right away is the namespace changed. Please note wp-json
is just the base path for the API itself, it is not part of a route. (I still add it to my tutorials in case you forget about it.) Now all built-in WordPress core routes have moved to the wp/v2
namespace, so the routes all start with wp/v2
.
New argument “per_page” and “filter[posts_per_page]” becomes a little buggy
WP API v2 uses a new argument “per_page
” in posts
route and set it to 10 by default. We can still use filter[posts_per_page]
to change the value of posts_per_page
in WP_Query
, but if we do so, the value of X-WP-TotalPages
in headers won’t get updated (because of how the value gets calculated) and our AngularJs pagination directive will be broken.
So when using filter[posts_per_page]
to set a max post count for an archive page, we need to hook to ‘rest_post_query
‘ to fix the pagination:
We’ll talk more about the function later.
“per_page” can’t be a negative integer so I stick with “filter[posts_per_page]”
A WordPress developer set posts_per_page
to -1
to get all posts from a query all the time (though it’s not the best practice). But now we have WP API v2, and to my surprise we can’t do the same thing with the new argument per_page
, because the sanitize_callback
function absint
will turn any negative value to positive.
Luckily we can solve this by simply setting filter[posts_per_page]
to -1
and get the results we expect.
The not so simple part is, if we force per_page
to a negative integer, the value of X-WP-TotalPages
in headers will be affected and turned to a negative integer too, which doesn’t make any sense at all.
That’s why in the gist above, at line 8 to 14, we need to set the per_page
argument to all post count ($query_result->found_posts
) instead of -1
. Now the X-WP-TotalPages
in headers will return the correct value “1
” when we ask for all posts in a certain query without pagination.
We can’t access the thumbnail url directly in the post JSON response
Back to WP API version 1, the “featured_image” property holds a huge object with very comprehensive information about that image. Now the same property name “featured_image” holds an integer and we can only access the attachment id. Since we display the thumbnail at the list page (main.html
), we need to update our code accordingly.
I’ve talked about this issue in my previous article “Adding Fields To The JSON Response Of The WP REST API” (scenario two), so we can just copy the gist and paste it to our functions.php
:
By hooking to rest_prepare_post
, we add an extra field “featured_image_thumbnail_url
” in our JSON response so we can access it without making extra API calls. After modifying the JSON response, we need to update the main.html
in our theme (at line 6-7, the thumbnail url changed):
“title”, “guid”, “content” and “excerpt” field in post response now return an object
Now when we need to access these values, we need to access their “rendered” property like title.rendered
, guid.rendered
, content.rendered
and excerpt.rendered
. And if you’re authenticated, you can also access their raw values with title.raw
, content.raw
etc.
No “is_image” property in media response but a new “media_type” property added
In this tutorial “Adding Slick Carousel To Your AngularJS WordPress Theme” I showed you how to grab all image attachments from a post to create a Slick carousel. The key point is we can tell if an attachment is an image or not by the “is_image
” property.
Unfortunately this property is now gone with WP API v1. I figured out how to bring it back with the gist below:
You don’t really need it if you’re not working with AngularJS. I use ng-if
in my content.html
to show or hide the image div, for ng-if
only takes an expression
which needs to be evaluated to true
or false
, I have to keep the is_image
property stay in the JSON response, rather than using the newly added media_type
.
Terms route changed and a new parameter “search” introduced
The changes for terms route made me confused most at first. The format in WP API v1 makes more sense to me that it shows the right hierarchy between taxonomy and their terms. For example, we get all categories with such route:
wp-json/taxonomies/category/terms
But in WP API v2, the route becomes:
wp-json/wp/v2/terms/category
wp-json/wp/v2/categories
The other significant change is we can’t use the filter
parameter to filter the terms. This was introduced in 1.2.0 but not working with WP API v2 anymore.
Version 2 takes only the following parameters when getting terms:
- per_page
- page
- search
- order
- orderby
- parent
I’m glad at least it takes search
parameter so I can still filter the terms by slug with wp-json/wp/v2/terms/category/?search=SLUG
wp-json/wp/v2/categories/?search=SLUG
. I need to do so because I set the category route in my AngularJS app to category/:slug
to get (theoretically) better SEO performance.
Final notice: modifying responses is discouraged
The last but not least notice is that modifying responses is discouraged by WP API. From the version 2 documentation, WP API team encourages us to use register_api_field
to add a duplicate field to the JSON response even if we just want to modify an existing one. And removing any field is definitely a no-no and dangerous thing you can ever do to your API response.
I definitely understand that and I’m sorry I don’t use the best practice in this tutorial. But if you only provide the API to third party applications that you know, I’ll say altering the JSON responses with filters like rest_prepare_post
is actually not that bad. Especially if you just want to alter the response for GET
endpoint in the route, but not other endpoints like POST, PUT or DELETE.
Thank you for spending time with me to learn my experience about upgrading the AngularJS theme to work with WP API v2. Any comment or tip you’d like to share is very welcome. I’m also glad to help if you have any question regrading my tutorials.
The final project files can be downloaded from the project GitHub repo. Just installing it like any WordPress theme, only you need the latest WP API plugin to get it work.
Talk soon!
Thanks 🙂 by default WP rest api give 10 post how can we retrieve more post than that like 20 or 30 post
Hi, the solution should be in this paragraph above: “New argument “per_page” and “filter[posts_per_page]” becomes a little buggy”.
Hi Yoren,
Thanks for a great tutorial,
I am currently getting an error Error: [ngRepeat:dupes] http://errors.angularjs.org/1.4.3/ngRepeat/dupes?p0=category%20in%20data.categories&p1=string%3A%3C&p2=%3C
at Error (native) …
When I changed over to wp rest api v.2 ?
Hi Rohan, regarding the migration between WP API v1 and v2, please refer to this post: https://1fix.io/blog/2015/09/29/wp-api-v2-angularjs-theme/.
Hey sorry I didn’t really answer your question from my previous reply. I can’t tell where the error comes from with these messages. If you still need my help, just pack your theme and send it to yoren[at]1fix.io.
I tried scanning for this error for far too long, turns out after migrating everything to v2 I forgot to re save the permalinks. Hope that helps.
Hey that’s pretty interesting and I can’t recall if I did that too. Thanks for this tip!
I am integrating AngularJS and the WP API. I started with version 1.23, but because I need to get all terms for a post, and that doesn’t seem at all possible with v1, I am trying V2. Everything works fine with V1, but getting a “Dupes” error in V2. Any ideas on that?
Do you know a way to duplicate the WP function, get_the_terms(ID) in the API, or do I have to create a custom route?
Thanks so much for any help. I have really made use of your tutorials to find my way with this project!
Hey Mary, I’m not sure where the “Dupes” error comes from, maybe Tim’s comment can be of use to you?
You can get the terms for a certain post with route like
http://demo.wp-api.org/wp-json/wp/v2/posts/178/terms/category
, just change the taxonomy at the end of the route.Glad I can help. Contact me if you need further assistance. Thanks.
Thanks for your help! The dupes error was because no data was being returned – I needed to toggle my permalinks, from switching to v2.
I got A LOT working, but now I am unable to load the featured image using your function – for a custom post type. It seems to only work for regular posts. I’ve looked all over for docs on these functions (rest_prepare_post , etc). Any ideas on adapting this to work with a CPT?
Thanks so much for your help.
Mary
Hey Mary, 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
.OK. I got it figured out. You have to append the function with the post type, a la “rest_prepare_portfolio’ – but it won’t work with a hyphenated post type, fyi. Seems we should be passing the post type to this function, so we’ll see if they work that out. I just changed my post type and removed the hyphen 🙁
Hi Mary, thanks for the notice. I haven’t noticed that a hyphen in the name of a CPT will cause a problem like this. It’s very interesting and actually makes sense because a hyphen is not allowed in a variable name in PHP.
I found a comment from WPMU https://premium.wpmudev.org/forums/topic/why-is-underscore-permitted-in-the-post-type-but-dash-is-disallowed#post-507002 shows how to set arguments for a CPT more properly. Just fyi.
Thank you so much Yoren, You did an amazing job here. Looking forward to see more posts about WordPress Rest API and Angular. Take care!
Hey Miguel, Thanks for your kind words. Your website looks really promising and I’m pretty sure you can target the right audience! Happy New Year!
Is there’s a way to automatically get the results of the next and previous posts in a specific order?
For a general WordPress theme you can. But with WP API, we don’t have a built-in route to do so. Need to build a custom route on our own.
Thank you very much for your tutorial. By following your series on AngularJS and WP-API I was able to get my self started on experimenting with AngularJS and WordPress.
I am trying to build a site that displays a list of posts on the front end using AngularJS and WP API v2. So far I was able to display the posts (all posts, no pagination) and was able to list the categories each post is assigned to.
Now, I want to be able to filter the posts by selecting from checkboxes. I need to be able to select one or multiple categories and show only the posts assigned to those categories that have been selected.
What is the best way to implement this? I am thinking of implementing Masonry to display the posts as tiles and a filter would be really nice.
By the way, since I am using a virtual host for my dev site (using angularpress.dev as domain), using fixed that nobase error.
Hey Aaron, Glad you have a very good idea to integrate AngularJS with WP API. You might need to create custom routes to make the multiple categories filter (tax query) work with WP API, or maybe just hook to the right filters can do the trick.
But regarding the AngularJS part, I’m afraid I can’t be helpful at this moment.
P.s. the last paragraph of your comments are keeping eaten by the comment form… sorry about that.
By the way, since I am using a virtual host for my dev site (using angularpress.dev as domain), using
fixed that nobase error.
Hello,
Thank you for your article !
Even if that’s not best practice with V2 I decided to go with rest_prepare_post to remove fields in the JSON response (it is just for displaying posts). I managed to get rid of many things but not of the “_links” field (with all its subfields) that I totally don’t need. I remove the other fields with “unset($_data[‘modified_gmt’]);” for example.
Any idea how to unset this as this is unnecessary for me and a pretty “heavy field” ?
Hey Fab, it’s pretty understandable because “_links” is not part of the response data, so we can’t use the filter to unset this very field.
I believe the _links field is like an universal field for any standard API. So we can’t modify it.
Hi,
Nice article.
I do have a question, there’s a white space in the beginning of the result. {{post.content.rendered}}, how can i remove this space??
Thanks.
Hi, Emmanuel, This should work for you:
http://stackoverflow.com/questions/26232826/how-can-i-remove-all-string-spaces-in-angularjs-binding
Hi Yoren,
I’m using wp-rest api plugin in wordpress. I’ve show all data through rest api, but i want to show 5 post per page (pagination). Pls help me. If you have some example it’s realy help to me.
Thanks,
Krishna Kumar Tiwari
Hi, you can use “filter[posts_per_page]=5” to get only 5 posts per page.
Hi,
Is there a way to use “Month and name” as Permalink and make it work with wp-api-v2? I followed your slug example but it only works when I use “Post name” for permalinks. Thanks in advance.
Hi Igor, please try this solution by Emil: https://1fix.io/angularjs-wp-rest-api/#comment-9070.
Hi YOREN CHANG,
in WP REST API V2 i am getting issue i am sure you can help.
http://localhost/wordpress/wp-json/wp/v2/posts?filter%5Bcategory_name%5D=css
giving all category response. is there any mistake in my route ? or something wrong in plugin.
i am waiting for your response 🙂
Thanks
Hey Mohd,
Just tested on WP API site and it seems worked fine. See:
https://demo.wp-api.org/wp-json/wp/v2/posts?categories=7
and
https://demo.wp-api.org/wp-json/wp/v2/posts?filter%5Bcategory_name%5D=apple-event
Both return correct posts in the very category. Need more info from you to help debug.
yes these both giving correct response but not mine.
on http://url.com/wp-json/wp/v2/posts?categories=id
giving all post record here is my json response
http://i.imgur.com/YnrjWtq.png
Hi Yoren, Impressive post.Have you ever considered Angular standalone front end with wordpress backend.I have a scenario where i have a wordpress admin and it manages content of side. But i want to add new apps which are basically angular js apps which could possibly talk to WP rest api once they are authenticated.These angular apps will basically do crud operations of different post types which are solely used by admins and will not get reflected on the sites.However these post types should show up in wordpress admin so that admin could take some action by looking at the data.The angular app is a web client and used on tablet by the user to fill information.Does this scenario make any sense and what would be the best way you think to implement this architecture wise.
Hi Riju, have you checked Michael Bromley’s https://github.com/michaelbromley/angular-wordpress-seed? I think it would be a good start if you’re looking for building a pure AngularJS application with WP API.
Such approach is also called “Headless / Decoupled WordPress”. Google for it maybe you’ll find more things of use to you.
Hi Yoren,
Excellent reference tutorials of tying up AngularJS with WordPress! I am working on importing my AngularJS SPA into WordPress but have slightly different approach with the routing. I am using UIRouter rather than ngRoute. This has a number of benefits but might be a bit tricky when it comes to displaying images for example. No PHP script works within the partials as they are pure HTML. How do you go about solving this kind of problem? The whole idea is to let the user update content, insert plugins within the code (i.e. Google maps) but this does not work for some reason. The partial get inserted into the index.php but still the scripts are not usable. Any ideas how to solve this problem?
Hi Nick, not sure if your question is about plugin compatibility? To me, when building such theme it almost guarantees tons of plugins won’t work with it. Although you can hack around it – with tons of efforts.
Thank you very much Yoren. Unfortunately that seems to be the case after a few days of efforts on my side to make it work. I handled the images but the plugin shortcodes are sitll not working. It was worth a try though. Learned something new 🙂
Hello Yoren, I’m not sure if it’s most fitting to ask this here, but could you please consider writing an article on how to establish post translation with angular-translate (https://angular-translate.github.io)? Translating app content defined in translation tables is easy, but dynamic loading different api urls for each language (ex. /{lang}/wp-json/wp/v2/) is currently beyond my ability and I’ve been struggling with this for weeks now. I’m using qTranslate plugin to translate wordpress content. Also changing routes accordingly is necessary.
Below I’m posting links to some solutions I’ve found at least giving some clues on how to resolve this matter. However a complete solution would be most appreciated – on the web there are only simple tutorials explaining how to translate angular app using angular-translate, but none of them is explicitly telling how to do it in a WP environment. Similar approach could be also used in an Angular2 theme. I really hope you will take a look at this.
https://github.com/tkssharma/Angular-Common/blob/master/src/controllers/langController.js
https://github.com/sitepoint-editors/multilingualwithangular/blob/master/js/app.js
https://github.com/Shelob9/angular-trans-exp/blob/master/app.js
Hey Kanabi,
Thanks for your suggestion! Just I don’t really pay attention to AngularJS anymore, thus I’ve stopped updating this series.
Maybe you can check with Josh and see what’s his take on this?
Hi there. Thanks for the Tutorial.
I’ve managed to display the featured image url within the WP API response, but WP debug keeps complaining about b>Warning: Missing argument 2 for `my_rest_prepare_post()`
What am I suppose to pass as the second parameter?
Regards,
Hi Adriano,
`my_rest_prepare_post ` hooks to `rest_prepare_post` which takes 3 arguments. So maybe check if you pass all of them?