Thursday, June 25, 2015

Structuring the User-Managed Content

One of the issues with using the post's content is that it is free-form, i.e., the user can format the content any number of ways. In some cases this is acceptable and in others it can break the screen's design.

Leveraging Advanced Custom Fields

We are going to leverage a popular plugin called Advanced Custom Fields (ACF): http://www.advancedcustomfields.com/.  This example uses version 4.4.2. This plugin will allow us to change tactics and use a single WordPress post (as opposed to posts grouped by category) to store one or more user-editable content elements for an AngularJS screen.

With ACF in place, we create a "home" post and create a new ACF field group called home.  In it, we create two WYSIWSY Editor fields called "top" and "bottom".  We apply the field group to the home post. To remove unused clutter from the page's administration screen, we hide all the other controls.

Now when editing the home post, one will only see the two fields "top" and "bottom".

Now we install yet another plug-in called WP REST API Custom Fields: https://wordpress.org/plugins/wp-rest-api-custom-fields/. This example uses version 0.2.

With this plug-in in place, a request to http://local.wordpress.dev/wp-json/posts/68 (post ID is 68) results in:

{"ID":68,"title":"home","status":"publish","type":"post","author":{"ID":1,"username":"admin","name":"admin","first_name":"","last_name":"","nickname":"admin","slug":"admin","URL":"","avatar":"http:\/\/0.gravatar.com\/avatar\/06e92fdf4a9a63441dff65945114b47f?s=96","description":"","registered":"2015-06-22T20:13:15+00:00","meta":{"links":{"self":"http:\/\/local.wordpress.dev\/wp-json\/users\/1","archives":"http:\/\/local.wordpress.dev\/wp-json\/users\/1\/posts"},"0":false}},"content":"","parent":null,"link":"http:\/\/local.wordpress.dev\/2015\/06\/25\/home\/","date":"2015-06-25T11:44:46","modified":"2015-06-25T11:46:51","format":"standard","slug":"home","guid":"http:\/\/local.wordpress.dev\/?p=68","excerpt":null,"menu_order":0,"comment_status":"closed","ping_status":"closed","sticky":false,"date_tz":"UTC","date_gmt":"2015-06-25T11:44:46","modified_tz":"UTC","modified_gmt":"2015-06-25T11:46:51","meta":{"links":{"self":"http:\/\/local.wordpress.dev\/wp-json\/posts\/68","author":"http:\/\/local.wordpress.dev\/wp-json\/users\/1","collection":"http:\/\/local.wordpress.dev\/wp-json\/posts","replies":"http:\/\/local.wordpress.dev\/wp-json\/posts\/68\/comments","version-history":"http:\/\/local.wordpress.dev\/wp-json\/posts\/68\/revisions"},"top":"TOP<\/p>\n","bottom":"BOTTOM<\/p>\n"},"featured_image":null,"terms":{"category":[{"ID":1,"name":"Uncategorized","slug":"uncategorized","description":"","taxonomy":"category","parent":null,"count":3,"link":"http:\/\/local.wordpress.dev\/category\/uncategorized\/","meta":{"links":{"collection":"http:\/\/local.wordpress.dev\/wp-json\/taxonomies\/category\/terms","self":"http:\/\/local.wordpress.dev\/wp-json\/taxonomies\/category\/terms\/1"},"0":false}}]}}


If one looks carefully, the "top" and "bottom" fields are injected under the "meta" object.

note: Looks like out of the box, WP REST API Custom Fields breaks the WP REST API for posts that do not have custom fields associated with it.

With this strategy, we can build our AngularJS app as in the example: http://jsfiddle.net/sckmkny/qp72hyf1/.

note: Because this example was developed to run against my local WordPress installation it will not work outside of it.

The Big Win with Advanced Custom Fields

As we indicated at the top of this post, the goal was to get away from free-form (or the WYSIWYG) entry by the user.  Here, we will change it so that we only deliver an image URL and a HTML encoded string through the API.

We create a post called "home structured".  We then create a ACF field group called "home structured" in a similar fashion as we did above, except the fields are called "image" and "text" and they are typed as such.

note: Because we are trying to limit the user, we set the text formatting option to "No formatting"; this HTML encodes the content.

{"ID":73,"title":"home structured","status":"publish","type":"post","author":{"ID":1,"username":"admin","name":"admin","first_name":"","last_name":"","nickname":"admin","slug":"admin","URL":"","avatar":"http:\/\/0.gravatar.com\/avatar\/06e92fdf4a9a63441dff65945114b47f?s=96","description":"","registered":"2015-06-22T20:13:15+00:00","meta":{"links":{"self":"http:\/\/local.wordpress.dev\/wp-json\/users\/1","archives":"http:\/\/local.wordpress.dev\/wp-json\/users\/1\/posts"},"0":false}},"content":"","parent":null,"link":"http:\/\/local.wordpress.dev\/2015\/06\/25\/home-structured\/","date":"2015-06-25T12:23:35","modified":"2015-06-25T12:27:30","format":"standard","slug":"home-structured","guid":"http:\/\/local.wordpress.dev\/?p=73","excerpt":null,"menu_order":0,"comment_status":"closed","ping_status":"closed","sticky":false,"date_tz":"UTC","date_gmt":"2015-06-25T12:23:35","modified_tz":"UTC","modified_gmt":"2015-06-25T12:27:30","meta":{"links":{"self":"http:\/\/local.wordpress.dev\/wp-json\/posts\/73","author":"http:\/\/local.wordpress.dev\/wp-json\/users\/1","collection":"http:\/\/local.wordpress.dev\/wp-json\/posts","replies":"http:\/\/local.wordpress.dev\/wp-json\/posts\/73\/comments","version-history":"http:\/\/local.wordpress.dev\/wp-json\/posts\/73\/revisions"},"image":"http:\/\/local.wordpress.dev\/wp-content\/uploads\/2015\/06\/frog.png","text":"test with html <b>"},"featured_image":null,"terms":{"category":[{"ID":1,"name":"Uncategorized","slug":"uncategorized","description":"","taxonomy":"category","parent":null,"count":4,"link":"http:\/\/local.wordpress.dev\/category\/uncategorized\/","meta":{"links":{"collection":"http:\/\/local.wordpress.dev\/wp-json\/taxonomies\/category\/terms","self":"http:\/\/local.wordpress.dev\/wp-json\/taxonomies\/category\/terms\/1"},"0":false}}]}}

With this strategy, we can build our AngularJS app as in the example: http://jsfiddle.net/sckmkny/mndq7wrj/7/.

Since we are not rendering HTML directly to the screen, we can get rid of ngSanitize.

note: Standard disclaimer... because this example was developed to run against my local WordPress installation it will not work outside of it.

No comments:

Post a Comment