One big opportunity for using JavaScript interfaces with the WordPress REST API is improving WordPress admin and settings screens. I wrote a post for Torque recently on using the REST API and jQuery to build settings screens. It was a good introduction but normally, I would use VueJS.
In the future, I’ll write more specifically about how to do that with VueJS, but I’d like to cover two important pain points I’ve dealt with getting to the point where this is my go to approach — compiling ES6 and template creation.
I’m a big fan of VueJS for a lot of reasons. It’s very easy to get started with and you don’t need to use any fancy tooling or ES6 to use it. That said, ES6 is a major improvement to the JavaScript language, so writing in ES6 makes working with VueJS or really any JavaScript so much better.
One thing I found tricky about applying what I learned about ES6 to a WordPress environment was that everything I learned about how to set up the tooling was assuming a traditional HTML + JavaScript project. That’s great for something totally decoupled from WordPress, but I wanted to do this right in the WordPress admin.
After some experimentation, I adapted the Gulp workflow that I use for minimizing JavaScript files in WordPress plugins to use Babel to compile ES6 to browser-safe ES5. It’s not going to get you hipster JS approved, but it works. I want to show you how it works and discuss a few other challenges of working with VueJS in the wp-admin such as how to build out your HTML.
Compiling ES6
ES6 is really great and I love working with it, but it’s not yet safe to use in all browsers. For now, Babel is the most reliable tool I’ve used to create browser-safe ES5 code. This may change soon, but for now, given that Internet Explorer is still a thing, this is a concern.
I use Gulp already for minifying and combining JavaScript files, so I wanted to stick with Gulp, but also have Babel, knowing that sooner than later, I would be able to remove Babel. Turns out using Babel with Gulp is relatively simple.
First, I added gulp-babel to my package.json:
npm install gulp-babel –save-dev
If you’re interested in my completed devDependencies for all of the required gulp libraries for this example, here they are:
"devDependencies": { "babel-preset-env": "^1.4.0", "gulp": "^3.8.11", "gulp-babel": "^6.1.2", "gulp-concat": "^2.5.2", "gulp-minify": "0.0.15", "gulp-minify-css": "^1.1.1", "gulp-sass": "^2.0.1", "gulp-sourcemaps": "^2.6.0", "gulp-watch": "^4.2.4" }
With that in place, I was able to add babel compilation to my standard JavaScript build process:
const gulp = require('gulp'); const watch = require('gulp-watch'); const minify = require( 'gulp-minify' ); const sourcemaps = require('gulp-sourcemaps'); const babel = require('gulp-babel'); const concat = require('gulp-concat'); gulp.task( 'adminJs', function () { gulp. src('./assets/js/admin/*.js') .pipe(sourcemaps.init()) .pipe(babel({ presets: ['env'] })) .pipe(concat('./assets/js/admin.js')) .pipe(sourcemaps.write('.')) .pipe(minify({ ext:'.min.js', noSource: false, mangle: true, compress: true })) .pipe(gulp.dest('.')) }); gulp.task('watch', function(){ gulp.watch( './assets/js/admin/*.js', ['adminJs' ]); }); gulp.task('default', ['adminJs' ]);
Setting Up Your Templates
VueJS, like Angular, uses HTML templates. If I wasn’t using Vue in a WordPress context, I’d use an HTML file or a .vue file for my templates. But in WordPress, I think it’s best to use a PHP file. Why? Two reasons: translations and setting up the default values.
I wrote about the various ways to include translatable strings in an AngularJS app before for Torque. I debated using PHP to render the templates or using wp_localize_script() to provide translated strings to JavaScript. These days, for Angular or Vue, I think that using PHP to create the template makes the translation process much easier.
Here is an example of a VueJS template for a post editor, as an HTML file that I copied from my earlier article introducing VueJS + the WordPress REST API
<div id="post"> <article> <header> <h1 class="post-title"> {{post.title.rendered}} </h1> </header> <div class="entry-content" v-html="post.content.rendered"></div> </article> <form> <div> <label for="post-title-edit"> Post Title </label> <input id="post-title-edit" v-model="post.title.rendered"> </div> <div> <label for="post-content-edit"> Post Content </label> <input id="post-content-edit" v-model="post.content.rendered"> </div> </form> </div>
You will notice that this has untranslateable strings for the form labels. I avoided that detail for simplicity’s sake, but I would never ship code like that. Here is the same code, refactored into a php file that provides those same strings via standard WordPress translation functions:
<?php if( ! defined( 'ABSPATH' ) ){ exit; } ?> <div id="post"> <article> <header> <h1 class="post-title"> {{post.title.rendered}} </h1> </header> <div class="entry-content" v-html="post.content.rendered"></div> </article> <form> <div> <label for="post-title-edit"> <?php esc_html_e( 'Post Title', 'text-domain' ); ?> </label> <input id="post-title-edit" v-model="post.title.rendered"> </div> <div> <label for="post-content-edit"> <?php esc_html_e( 'Post Content', 'text-domain' ); ?> </label> <input id="post-content-edit" v-model="post.content.rendered"> </div> </form> </div>
That solves my first concern. But I have another concern I’d like to solve. In the code example, I showed in that original article, the initial state for the post was setting by making a GET request to the WordPress REST API. To me, this makes no sense. Why make a request to get the page, then a request to get post content?
I wrote awhile ago about using the WordPress REST API without an extra HTTP request. I encourage you to review how that worked. Also, take a look at how the new WordPress post editor prototype Gutenberg uses the WordPress REST API, without an extra HTTP request, to get the data for an existing post into a window-scope variable that can then be used to set the initial state for React.
Here is an example function, based off of Gutenberg’s code to enqueue a JavaScript file and place a saved post’s data into the JavaScript object window._exampleEditor:
<?php function example_setup_editor( WP_Post $post = null ){ if ( ! is_object( $post ) && isset( $_GET[ 'post_id' ] ) ) { $post_id = (int) $_GET[ 'post_id' ]; $post = get_post( $post_id ); } if( ! is_object( $post ) ){ return; } $post_type_object = get_post_type_object( $post->post_type ); if ( current_user_can( 'edit_post', $post->ID ) ) { $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/%s/%d', ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name, $post->ID ) ); $request->set_param( 'context', 'edit' ); $response = rest_do_request( $request ); if ( $response->is_error() ) { return; } wp_enqueue_script( 'your-handle', '/path' ); wp_add_inline_script( 'your-handle', 'window._exampleEditor = ' . wp_json_encode( $response->get_data() ) . ';' ); } }
With this in place, I could setup a VueJS component to use the template I showed above.
new Vue({ el: '#post', data: function () { return { post: window._exampleEditor } } });
A little bit of PHP goes a long way to improve a JavaScript-driven interface sometimes.
Examples And Making It Your Own
I’ve done a few plugins that use VueJS in the admin screen so far. Caldera Forms Pro’s client plugin is the only one using ES6. I encourage you to check out the source for that on GitHub. For examples using React, you should read Kadam White’s WP Notebook plugin or Gutenberg.
Now it’s your turn. Find a part of your plugin or theme or site’s admin that you can improve with VueJs or React and use it as a way to learn. Then start using these skills in the front-end to progressively enhance your site.
1 Comment