The tutorial will show you how to setup and work with Angular.js inside of a WordPress plugin to create a stand-alone, API powered, Angular.js single page app or microsite that is available at a defined path of a WordPress website.
Live Demo GitHub Source
By the end of this tutorial you should grasp a few key things about working with Angular.js inside of WordPress. Using a self-installed WordPress install as a backend API service for serving HTTP requests to a front-end Angular.js client is a powerful combination. If you’re an eager beaver you can dive right into the sample plugin on GitHub which provides the source for the working demo. This example app should demonstrate the following concepts I commonly come across when building Angular apps in WordPress:
$resource
service/api/**
routes to reduce requests per second (when it makes sense to)If there’s a common scenario I didn’t mention that you would like to recommend please let me know in the blog post comments.
pushState
routingTo begin we we’ll start by creating a fresh WordPress plugin. I’m assuming you know how to do this already, but if not then I recommend reading the detailed plugin guidelines provided on WordPress.org. This plugin will serve as a backend to our angular app, and will handling the following logic for us:
https://github.com/Kevinlearynet/WordPress-AngularJS-Plugin**
. The trailing wildcard is critical for supporting HTML5 pushState routing within our Angular app.There are a few key benefits to building both the backend and front-end inside of a single WordPress plugin:
wp_localize_script()
when users are logged in, and various other things.All of the logic described in this tutorial could be used within a WordPress theme as well, the same concepts apply.
Our WordPress plugin defines the URL where our Angular app will load based on the path of the plugin directory. The intercept_wp_router()
method is applied to the do_parse_request
filter to handle this:
/**
* Intercept WP Router
*
* Intercept WordPress rewrites and serve a
* static HTML page for our angular.js app.
*/
public function intercept_wp_router( $continue, WP $wp, $extra_query_vars ) {
// Conditions for url path
$url_match = ( substr( $_SERVER['REQUEST_URI'], 0, strlen( $this->base_href ) ) === $this->base_href );
if ( ! $url_match )
return $continue;
// Vars for index view
$main_js = $this->auto_version_file( 'dist/js/main.js' );
$main_css = $this->auto_version_file( 'dist/css/main.css' );
$plugin_url = $this->plugin_url;
$base_href = $this->base_href;
$page_title = 'WordPress Angular.js Plugin Demo App | kevinleary.net';
// Browser caching for our main template
$ttl = DAY_IN_SECONDS;
header( "Cache-Control: public, max-age=$ttl" );
// Load index view
include_once( $this->plugin_dir . 'views/index.php' );
exit;
}
If you want to change the base URL for your app to something custom you’ll need to change the value of the public variable base_href
. This is set in the __constuct()
method of the ngApp
Class. That’s a mouthful, but basically you would find and modify this line within the plugin:
dirname( __FILE__ )
In the case of this tutorial, our main plugin file is wordpress-angular-plugin/wordpress-angular-plugin.php
so the Angular app will load at https://github.com/Kevinlearynet/WordPress-AngularJS-Plugin
out of the box. You can change this whatever you like in the plugin to customize the URL.
Once you load up https://www.yoursite.comhttps://github.com/Kevinlearynet/WordPress-AngularJS-Plugin
you should see the same Angular app demo currently available at:
kevinleary.nethttps://github.com/Kevinlearynet/WordPress-AngularJS-Plugin
Google Chrome and other browsers will cache our *.css and *.js for an indefinite period of time. If we make changes to our Angular app’s code, or our LESS stylesheet, browsers won’t know the file has changed and could serve the old, previously cached version of the file to repeat visitors. For this reason, it’s very important that we add version strings to static resources, or in our case the /dist/js/main.js
and /dist/css/main.css
files. This is especially important for single page apps because we are effectively loading EVERYTHING in the browser.
Luckily, I’ve included a setup in this plugin that will handle this for you automatically. This is the only thing that PHP is actually used for in the index.php
template.
Here’s the method that handles this for us:
/**
* Auto-version Assets
*/
public function auto_version_file( $path_to_file ) {
$file = $this->plugin_dir . $path_to_file;
if ( ! file_exists( $file ) ) return false;
$mtime = filemtime( $file );
$url = $this->plugin_url . $path_to_file . '?v=' . $mtime;
return $url;
}
Using PHP’s filemtime()
function we check the modified time of the CSS and JS files, and then we add the timestamp returned to the end of each file as a “version string” like this:
/dist/css/main.css?v=1497114238
/dist/js/main.js?v=1497114238
Now you’ll always have up to date assets loading for your users!
Now that we have the basic structure for an Angular app setup within a WordPress plugin let’s look at how to make requests from client (Angular) to server (WordPress) by defining a few custom HTTP API routes. For demo purposes I’ve wired together a backend API that will:
/api/weather/{latitude:longitude}/
In a real-world scenario we could just do this inside of Angular entirely, but it serves as a good example to cover common situations where:
In the context of our demo app, an API request is made to the weather API endpoint defined in our plugin to retreive the weather for a users location. This provides a basic demonstration of how to write a PHP backend service that processes input from our Angular app.
Important Note
I am deliberately NOT using the official WP REST API here. I personally believe in building minimal systems that solve specific problems, I think it make a big difference in terms of maintenance, sustainability and security. This is entirely my own opinion, but I beleive it’s better to build your own microservices like this rather than load in the entire WP JSON api for many circumstances.
The Angular.js app is currently using Angular 1. Most of the project I work on at the moment are still using Angular 1, only a few have made the switch to 2. Because some of this code is directly pulled from those projects I found it easier to work with Angular 1. In the future I will update this to Angular 2 on another branch.
The Angular 1 app in this demo is very basic, but it does handle a few important things well:
$resource
service to interface with a custom HTTP API we’ll define in WordPressOur Angular app is served from a single file: ./views/index.php
. This is where we define our [ng-app]
, the structure of our HTML doc, and the <ui-view />
directive provided by ui-router. This tag will specify where every view inside of our Angular app will load when the URL changes or initially loads.
Using *.php and not *.html allows us to easily pass values from WordPress into Angular by adding them to an inline JSON object before we load main.js
. This is a similar approadch to using the wp_localize_script()
function in WordPress to pass PHP values from a plugin or theme into JS.
The Angular app is served from a single file within the plugin: ./views/index.php
. This is where we define our [ng-app]
, the structure of our HTML doc, and the <ui-view />
directive provided by ui-router. This tag will specify where every view inside of our Angular app will load when the URL changes or initially loads at or below the app path defined by our plugin.
This means that anything underneath this URL is handled by Angular.js routing and the ui-router module. We’ve specifically setup the way we route to and load in our /views/index.php
file to support this structure. This will handle the WordPress angular routing in a seamless way so that:
It is only possible to do this if EVERYTHING underneath our https://github.com/Kevinlearynet/WordPress-AngularJS-Plugin
page is handled by Angular and UI router.
UI-Router defines routes for everything served by Angular undereath the https://github.com/Kevinlearynet/WordPress-AngularJS-Plugin
directory. Here’s what a typical route definition looks like.
/**
* Home View
*/
// Route
mainApp.config( function( $stateProvider, WP ) {
$stateProvider.state( {
name: 'home',
url: '/',
templateUrl: WP.plugin_url + '/views/home.html',
controller: 'homeCtrl'
} );
} );
// Controller
mainApp.controller( 'homeCtrl', function( $scope ) {} );
This is pulled right from the contents of the /views/home.html
template. The mainApp
is our main ng module defined in /js/main.js
with angular.module()
.
When you look at the source of /js/main.js
you’ll see a number of lines that look like this:
// =require ../vendor/angular-sanitize.js
// =require ../vendor/angular-animate.js
// =require ../vendor/angular-ui-router.js
These define external JS files that we want to include in our JS app. These are handled by gulp during the compile process with the help of the gulp-include plugin. For now I find that this approach is straight forward and easier to work with than Webpack or CommonJS, but if you find that your /dist/js/main.js
file is getting too large then it may be best to work with Webpack instead.
More information on the build process can be found in the compiling with gulp section.
The plugin uses Gulp to compile our CSS/JS and also prepares our HTML, with a few smart additions.
I recommend that you use the watch process to build your CSS/JS on-demand as it changes. This provides a faster web development workflow. To start gulp in watch mode open a Terminal window and go to the directory where you’ve cloned this plugin. Enter the gulp
command in your prompt and Gulp will begin watching for JS/CSS changes.
If you’ve just cloned the repo you can build everything with the gulp build
command. This will run through every process needed to everything we need to serve our micro app (CSS/JS/HTML).
The js-main
gulp task uses a few gulp plugins to compile and prepare our JavaScript files for the app. This part of the turorial is entirely opinionated, it’s a set of tasks that I commonly use so I’ve included them here.
The less
task is pretty straight forward. It compiles LESS to CSS, with the following helpful additions:
The combination of an Angular.js front-end and a WordPress API backend provides a powerful framework for building all kinds of find things. Hopefully this tutorial gives you a few ideas about how work with the two technologies in your projects. I do myself all the time. If you have any questions, comments or feedback please let me know in the comments below.