This past weekend I was at WordCamp San Diego, and it was fantastic. I was not just a speaker this time, I was a sponsor. It was a great weekend filled with talking with old friends that are in the community, as well as meeting tons of new ones. As you can guess, my talk was about AngularJS, WP-API, and how to build out single page applications using WordPress (and why). I think my talk went well, and got a lot of great feedback about it. However, I feel like I have been talking about these core subjects for a while. However, after my talk Jon Brown, a master in the WordPress community, and WordCamp organizer (Maui), told me that I should do a talk about how to integrate an AngularJS (or other JS app) into WordPress without it being a single page application.
I realized I have never shown or done this before, my main examples were always “well you have a page, and that page is now your whole website / app”. Before I get into more of the code and my own example, lets just circle back to what is a Single Page Application.
CodeCavalry, a Single Page Application built on WordPress
CodeCavalry.com is a site built for ARC(CTRL), it is a place for users and consultants who need quick coding help can get help fast and easy. Unlike some of the other code helping sites out there, CodeCavalry is focused on speed of getting help, and clean easy to understand UI/UX. We felt like an easier to use website was needed, especially since we wanted to first target the WordPress consultant community.
CodeCavalry has a few pages on the website, but for the most part the most useful parts are all running on a single page. In the WordPress dashboard, there are only a few pages that exist, even though there are many more pages that you might encounter on the site as you are using it.
You see that page called “app”? That one page runs the User Dashboard, all the sessions (help sessions), edit profile page, Knights page, and many more. Anytime you are around the site, check out the URL, if it says “codecavalry.com/app…” you are still only technically on that 1 WordPress page. AngularJS routing helps us do the magic with the rest. That is the definition of a Single Page Application
However, what if your application that you are building in AngularJS (or other JS framework) isn’t as robust, or doesn’t take up your entire site? It may be a simple application, or a simple piece of functionality that has no more then 1 view, so what then? Do you really need to do the same thing? Not at all.
Embedding or Injecting an AngularJS application into your template
It is completely possible to inject or have an AngularJS application running alongside your normal WordPress content, but still have all the benefits of AngularJS template language, and functionality. As you may know from my other posts, AngularJS is very powerful, mix that with the WP-API and you have the equation to create very powerful website applications with WordPress.
Instead of building out a new app to show you, I am going to use an existing application I already built and was showcased in my last blog entry about using Firebase and WP-API for instant interactions, more specifically a text chat.
First I’m going to make sure that my plugin – Chat Rooms powered by Firebase – is installed, that way I don’t have to actually do much extra coding here 🙂 I also need to install the AngularJS for WP plugin, which the chat plugin leverages.
Once that is all installed and activated, we are going to add in a chat room into a page anywhere we want. I’m going to do this so that you can follow along and inject the AngularJS app into the WYSIWYG editor using the “text” tab.
- Create a new Chat room
- Copy the POST ID from the URL (this is important)
So I created a new chat room called “My Awesome Chat Room” and it has a post ID of 1497, now to grab the code. I just opened my plugin and copied the code, but this is your template for your AngularJS plugin
<div ng-app="chat_app" ng-controller="chat_controller" ng-init="startChat(1497)">
<article ng-repeat="msg in fireChat.chat">
<form id="fire_chat_form" ng-submit="newChat()">
<input ng-model="msg.name" placeholder="Name" />
<textarea ng-model="msg.msg" placeholder="Message"></textarea>
<input type="submit" value="chat" />
So what are we looking at, what makes this Angular-ific?
This is the most crucial part of the code. This defines out the application which was defined in the AngularJS code. To start any AmgularJS project, you have to define your app, or your main angular module
var chat_app = angular.module( 'chat_app', ['firebase'] );
chat_app is now defined as the app, so we can add controllers, and other logic to it, and on the front end ng-app knows which app to run based on this definition.
ng-app cannot be nested, make sure to pay attention where you put it
Keep in mind that you can have more then one “app” on a page or even website, however you cannot nest them (easily). I ran into this issue with my AngularJS for WP plugin + Chat plugin working together. I originally had the AngularJS for WP ng-app defined on the <html> DOM object, which means you could not have any ng-app within. It is also hard to inject or redefine an app, especially in my case since I needed to inject “firebase” but did not want to inject it into the AngularJS for WP plugin due to it not needing that library. I reworked both plugins so that they only initiated ng-app when needed.
On CodeCavalry I have it defined on the <html> DOM object, but since there is only ever 1 app running, there is no problem there
Within any app you may have multiple controllers. Controllers is where all the data logic and interaction is done. You do not want to have your whole app in 1 controller (unless its tiny) because you wan to to have some sort of separation of functionality. When looking at CodeCavalry.com there is a controller for a session, there is a controller for the dashboard, there is even a controller for the New Session modal. Functionality is kept separated mainly to help keep your variables, functionality, and data unique. You don’t need the chat data into another part of your application, so why load it? Each controller gets a unique $scope object, which is where data is stored and shared with the UI.
Not every AngularJS controller or app has an init function, and ng-init doesn’t even need to be on the controller level, it can be on any element that you need to have a function run upon initialization. In this case the ID was important, the template file is PHP, it upon render of the HTML, has the ID stored in an the $post object that WordPress creates. The ID for the post is crucial in making the whole chat system work, but there is no easier way to get it then to have PHP put it into an initialization function. In the example above I typed in the chat room post ID, but in the actual plugin PHP template, it looks more like
$template .= '<div ng-app="chat_app" ng-controller="chat_controller" ng-init="startChat('.$post->ID.')">';
You can see the difference, in PHP I can echo out HTML (which PHP renders server side) with the ID already there, so the ng-init function can run based on that POST ID.
Another example of this is on a CodeCavalry expert profile page – like mine – because I used the author template (author.php) I had to initiate the AngularJS template with the ID of the user for that profile. I used some tricky Rewrite API logic to get rid of “author” in the URL, then grabbed the user object based on the user_nicename found in the URL. For those of you who like that piece, go ahead and thank Matt Cromwell for suggesting it to me at WordCamp Los Angeles 2014.
Injecting into a php template
There isn’t much more to it then what I already showed you. In any template you have for your theme, either paste in the HTML you need, and make sure at minimum that ng-app and ng-controller are set. Remember, ng-app can be defined on a parent DOM element, or on the element itself.
You can see an example of how it all gets put together and works on the chat demo site