In this article, I’m going to walk through the fundamentals of creating a better admin interface for plugin and theme developers using AngularJS. I’ll also demonstrate how you can take it to the next level.
Before you begin, you must determine what it is you are administering on the site specifically. To keep this simple, let’s say your plugin creates a custom post type (CPT) that is hidden from the sidebar. This use case is simple — you have a CPT to store data, but you don’t want to use the default WordPress edit screen. Instead, you want to associate data to it using post meta but also have the UX be unique and easy for the user.
It’s All About UX
This is one of the real powers of the REST API and what gets so many developers excited. So let me reiterate, think your UX through before you begin.
Advanced Code Warning
Preflight Check: Create A plugin And Add Some Content
Note: This is just a foundational step that’s necessary when starting from scratch. If you already have a plugin or theme with content (or data), you can skip this section.
Before you get started, you need data to look at and manipulate. Start by creating a plugin, then add a Post Type and some posts. I am using a site_option to save the content that has been created.
You can view my code for CPT creation and content creation on GitHub.
When I created the CPT, I made sure to set show_in_menu to false so that it doesn’t show up like every other CPT in the left sidebar of the admin dashboard.
The next step is to get your base UI and DOM set up for your app to take over. I’m not going to go into the details of creating an admin menu page or enqueueing scripts, but do have my code online.
Admin Menu Page
The menu page DOM is pretty simple. You have a navigation bar and your base content. If you are just starting, I recommend skipping the nav until all states are ready to go. This can be one of the last things you code.
Enqueue Admin JS
I have enqueued admin-app-scripts.js, which is a concatenated file comprised of a few other files including angular, angular-ui-router, angular-resource, and my admin-app. I use Gulp to handle the concatenation. If you want to use my setup, you will need to run gulp js in your command line after you have installed all npm dependencies using the npm install.
To see the files that are being concatenated, or to add new ones, go to lines 6-11 of gulpFile.js file.
HTML Template Directory
Step 2: Customize The API For Your Data
Before you create your app, you need to make sure the API has the necessary endpoints to easily grab and manipulate the data.
If you are just creating an app for default post types (post and page), then feel free to skip this step.
Step 2a: Custom Endpoints Vs. Default Endpoints
When I first started writing this article and creating the code for it, I created a custom endpoint to showcase how it works, however, I was quickly corrected by Josh Pollock. This is because if you have a simple CPT and your actions are just to edit, delete, and create, then you should be using default functionality with default built-in endpoints. Adding custom fields isn’t supported in WordPress 4.4, so you will need download the REST API v2 beta.
Adding REST Capability To A CPT
Adding RESTful support to an existing CPT is easy. If you have access to the CPT creation, just set the show_in_rest parameter to true, if you don’t have access to the CPT creation code, just follow my example here.
Using this example, you can add REST support for multiple CPTs by adding your CPT slug into the $post_type_names array.
Custom API Fields
By default, the REST API does not include meta data from a post into the response, so we will need to add custom fields. First, you will need to register the fields, then create two callback functions for handling GET and POST requests. These callback functions handle retrieving and updating the post meta.
First, you will need to register the fields, then create two callback functions for handling GET and POST requests. These callback functions handle retrieving and updating the post meta.
In my example, I specifically grab two post meta fields, and, on save, I loop through. This is an easy approach, however, for a production application, I recommend creating a custom field for every custom post meta. That way each one is modular and you can do any logic to the field (checking for type, evaluate the data, etc.) independent of any other custom meta field.[crayon-5a6147550588a007072090/]
If you want more functionality other than simply saving a post and some post meta, you may want to create a full custom endpoint for everything. You can create custom endpoints with this quick two-step process. First, register the routes with a namespace, and then create callback functions based on the HTTP method. I have left the code in for my basic example of this here.
Step 2b: Testing Your API
Once you have completed the first part, you should be able to easily test if it works. The fastest way to do so is to go to yourdomain.com/wp-json/ to see if you can either find the namespace you have declared your custom endpoint or find your CPT slug. In my code example, I find “book” in the “wp/v2” namespace, so I know I can go to domain.com/wp-json/wp/v2/book to see a list of the latest “book” posts. It works — huzzah!
If you want to test everything, you can use something like the app for Chrome, Postman REST API, which allows you to check calls like POST directly without writing any code. Remember, you may need to use that nonce saved in the localized object.
Step 3: Create The App, UI States, Factory, And HTML Filter
- main page (general info page)
- listing page (to list posts from our CPT)
- detail page
- and edit page.
Declaring The App And Dependencies
Go back to the menu page you defined in php. You will notice in my example that the container div has ng-app — this needs to match the name of your Angular app. Since you are going to use the UI router and ng-resource, you will need to include these external dependencies in your app. Your final code should look something like this:[crayon-5a6147550589a906078304/]
Setting Up The Routes And Templates
Now it’s time to set up the routes. Using the UI router, you will define a few states and their associated controllers and templates:[crayon-5a614755058a2928733450/]
You can see the four states listed above defined using the UI router and their corresponding HTML template. Make sure that you have the HTML files (or create them) in the right place so you don’t get any 404 errors while trying to view your app.
Setting Up The Filter And Factory
You will only need one filter and factory for the app. If you want to work with more than one endpoint, then you will need to create a new factory. The filter uses an injectable $sce to trust HTML text and display rendered HTML rather than just the HTML as a string.
You can see that I injected the nonce into the factory as well so I don’t have to do it on a call-by-call basis. Make sure to alter the route in the factory based on your endpoint, /book. It will not work for you if you do not have a ‘book’ CPT.
Step 4: Create The Controllers
Now that you have everything in place, the last piece of the puzzle is to get the controllers going. If you want to test your app to see if it is working, remove any controller calls in the Angular UI state, similar to how my example for “main” state doesn’t have a controller. If you change the HTML in each of the HTML templates and maneuver to the URL, like /book, you should see the correct HTML template.
Controllers set up the data for each template or view. Starting with the list controller, create the controller and make sure to inject your factory.
The list controller is pretty basic. All it does is query the endpoint for the latest posts, which I then store to $scope.books. In the template, I have a ng-repeat that repeats through all the books to display a list of all of the posts.
That’s it! Your list controller is done!
Repeat the process for the remaining controllers and you should end up with the four states as defined in the UI router.
AngularJS Isn’t The Only JS Framework
I personally love AngularJS, and built this application using it. This JS framework, however, may not work best for you.
You can check out the organization of the repo located here for other examples.
I am working on a few other versions — the next is ReactJS. Feel free to create your own version using whatever framework you enjoy working in, and submit a pull request.
The Future Is Near
Why Is This Good?