Dynamic Pages with AngularJS and WordPress

Table of Contents:

  1. Introduction
  2. Pretty URL’s with HTML5 History API
  3. Catch-all Route
  4. Fetching Dynamic Data
  5. Dynamic Page Binding
  6. Summary
  7. Other Posts in this Series

Introduction

In part 1 of this AngularJS and WordPress series we wired up a Yeoman AnguarJS app to a WordPress back-end with WP REST API and rendered the title and content of our WordPress back-end as the main page of our AngularJS app. This was a great first start, but mapping our WordPress pages to AngularJS, page-for-page, isn’t terribly useful.

In this instalment I will show you how to dynamically render any page from WordPress based on the URL in our AngularJS app. This becomes useful when your team’s Content Creator creates new pages, we don’t have to add anything new to our front-end.

Let’s get started.

Pretty URL’s with HTML5 History API

First things first, we want our URL’s to clean. By default, AngularJS uses a # in the URL to determine what part of the application to render. But instead we can use the HTML5 History API mode to give us clean URL’s. To do this there are three basic steps; 1) enable the feature in AngularJS, 2) telling our connect server to redirect all requests to /index.html and 3) setting a base url in our index file. This will allow our application to handle all incoming requests.

Enable HTML5 History API

  1. Open app/scripts/app.js and add $locationProvider to the config callback:
    .config(function($routeProvider, $locationProvider) {
    
  2. Then just below that line, add the following:
    $locationProvider.html5Mode(true).hashPrefix('!');
    

Redirect all traffic

  1. For this we need a new npm module, connect-modrewrite. In the terminal:
    $ npm install connect-modrewrite --save-dev
    
  2. Then in your Grunfile.js, after appConfig (shown below):
    // Configurable paths for the application
    var appConfig = {
      app: require('./bower.json').appPath || 'app',
      dist: 'dist'
    };
    
    // NEW CODE
    // to allow rewrite for html5 pushstate functionality
    var modRewrite = require('connect-modrewrite');
    
  3. And in your connect.livereload middleware section:
    livereload: {
        options: {
            open: true,
            middleware: function (connect) {
                return [
    
                    // NEW CODE
                    modRewrite([
                        '!\\.html|\\.js|\\.css|\\.png|\\.jpg|\\.jpeg|\\.gif|\\.webp|\\.svg|\\.woff2|\\.woff|\\.ttf$ /index.html [L]'
                    ]),
    
                    // etc, etc...
    

Set a base URL

  1. Open app/index.html and add a base URL somewhere in the head tag:
    <base href="/">
    

Before carrying on, now would be a good time to fire up your application to make sure your changes worked (or at least didn’t break anything). If everything was done correctly, you should be able to navigate to http://localhost:9000/about and see the following:

yo-angular-html5-history-api

Catch-all Route

Now that we’re all set to use pretty URL’s, let’s set up a catch-all route that will handle any otherwise un-handled request and attempt to fetch a matching page from our API.

  1. With yeoman, create a new route:
    $ yo angular:route dynamic
    

    This will add a new /dynamic route to our $routeProvider config, add a new dynamic.js controller to app/scripts/controllers and add a new dynamic.html view to app/views. In its current form, this will only handle requests to http://localhost:9000/dynamic. Go ahead and try it. The page should say, “This is the dynamic view.”

  2. This is a great first step, but we want our catch-all route to handle any URL, so let’s replace /dynamic with an optional route parameter. In app/scripts/app.js:

    // change this:
    .when('/dynamic', {
    //to this:
    .when('/:routename?', {
    

    Now we should be able to navigate to any un-handled route and have our dynamic view rendered. For instance, go to http://localhost:9000/foo-bar and you should still see the dynamic view.

    yo-angular-dynamic-view-foo-bar

Fetching Dynamic Data

We now have a catch-all route to handle any page request, but now we need something to show there. The place to do this is in the route’s resolve option. That way we can fetch the necessary data prior to rendering the page. This will prevent any flash of unstyled content (FOUC).

  1. In app/scripts/app.js, in our catch-all route:
    .when('/:routename?', {
      templateUrl: 'views/dynamic.html',
      controller: 'DynamicCtrl',
      controllerAs: 'dynamic',
      resolve: {
          pageContent: function getPage($route, $http) {
              var pathname = $route.current.pathParams.routename;
    
              return $http.get('//yoursite.url/wp-json/wp/v2/pages/?filter[name]=' + pathname)
              .then(getPageSuccess)
              .catch(getPageError)
    
              function getPageSuccess(response) {
                  if (response.data && response.data[0]) {
                      return response.data[0];
                  } else {
                      throw response;
                  }
              }
    
              function getPageError(reason) {
                  console.log('error: ', reason);
              }
          }
      }
    })
    

    This should look familiar as we wrote something very similar to this is our introduction article, under the Fetching Live Data heading.

  2. Next we’ll need to inject this resolved data into our dynamic.js controller. Open app/scripts/controllers/dynamic.js, inject the pageContent and console log it;

    .controller('DynamicCtrl', function (pageContent) {
        console.log('pageContent: ', pageContent);
    });
    

    Now if you navigate to your test page made in the first article, http://localhost:9000/test-page, you should see the resolved data in the console.

    yo-angular-catch-all-resolve

Dynamic Page Binding

Is this starting to look familiar? Yeah fetching for any page is the same a fetching for one, so this really is a repeat of the last article with a few twists.

  1. Just like before, we need to set some scope variables, this time using dynamic. In app/scripts/constrollers/dynamic.js:
    .controller('DynamicCtrl', function (pageContent) {
        var dynamic = this;
    
        dynamic.title = pageContent.title.rendered;
        dynamic.content = pageContent.content.rendered;
    });
    
  2. Open app/views/dynamic.html and replace its contents with the following:
    <div class="jumbotron">
        <h1 ng-bind-html="dynamic.title"></h1>
    </div>
    <div class="row marketing" ng-bind-html="dynamic.content">
    </div>
    

    Now have a look at http://localhost:9000/test-page.

    yo-angular-dynamic-render-1

    Go ahead and make any other page in WordPress, navigate to it, and you’ll see that automatically your AngularJS application knows how to fetch and render the data without making a new controller or view for it.

    yo-angular-dynamic-render-2

Summary

In this article we went over setting up your Yeoman AngularJS app to use HTML5 History API. Then we made a catch-all route to deal with dynamic routes and finally bound those dynamic values to our view. You now have one route, one view and one controller that can handle as many pages as your WordPress site contains.

In future posts I will cover further details like dynamic navigation systems, site configuration, page specific configuration and more. Never stop building.

Other Posts in this Series

 

seydoggy

 

Leave a Reply