Dynamic Sitemap with AngularJS and WordPress

Table of Contents:

  1. Introduction
  2. Create the Route
  3. Call Up the Sitemap Data
  4. Bind the Data
  5. Add Sitemap to Sitemap
  6. Add Sitemap to the Footer
  7. Summary
  8. Other Posts in this Series

Introduction

In part 1 through 4 of this AngularJS and WordPress series we wired up a Yeoman AnguarJS app to a WordPress back-end, set up a catch-all route, controller and view that could fetch our WordPress pages dynamically and created a dynamic, responsive navigation with some sitemap json.

In part 5 I will show you how to reuse that sitemap data to create an always up to date, dynamic sitemap that is in sync with your navigation. This is a super short article.

Create the Route

  1. In the terminal, enter the following yo command:
    yo angular:route sitemap
    

    This will create the following files:

    app/scripts/controllers/sitemap.js
    app/views/sitemap.html
    test/spec/controllers/sitemap.js
    
  2. Yeoman will also automatically create a new route to handle /sitemap in app/scripts/app.js and wire up your new files in app/index.html. However, Yeoman will have added our sitemap route after our default catch-all route which will try to handle out sitemap route for us. We don’t want this.

    We need to move our sitemap route before our catch-all route, like this:

    .when('/sitemap', {
        templateUrl: 'views/sitemap.html',
        controller: 'SitemapCtrl',
        controllerAs: 'sitemap'
    })
    .when('/:routename?', {
        templateUrl: 'views/dynamic.html',
        controller: 'DynamicCtrl',
        ...
        // rest of code
    

    Now when you navigate to localhost:9000/sitemap you’ll see the text, “This is the sitemap view.”

Call Up the Sitemap Data

In part 3 we made a sitemap factory so that we could fetch the sitemap data once and reuse as needed. We also set that factory to fire on app run and broadcast when it was finished. And just like in our navigation directive, in our sitemap controller we’re going reuse that data by using the factory and listening for that broadcast to set a bound variable.

  1. Open app/scripts/controllers/sitemap.js and inject $scope and our sitemapService:
    .controller('SitemapCtrl', function ($scope, sitemapService) {
    
  2. In the body of the callback function, replace the boilerplate code with this (note that we’re using the more conventional vm now, which stands for view model):
    vm.sitemap = sitemapService.get();
    $scope.$on('sitemap:updated', function (event, data) {
        vm.sitemap = data;
    });
    
    

    This will get the sitemap data stored in our sitemapService factory, but in the even it changes at any point, we’ll listen for a sitemap:updated event and update our vm.sitemap object accordingly.

Bind the Data

Now that we have our sitemap data available, we can bind it to our view.

  1. Open app/views/sitemap.html and replace the boilerplate html with the following:
    <h1>Sitemap</h1>
    <ul>
        <li ng-repeat="page in sitemap.sitemap">
            <a href="{{ ::page.path }}" ng-if="::page.path != ''" title="{{ ::page.name }}" ng-bind="::page.name"></a>
            <div ng-if="::page.children">
                <span ng-if="::page.path == ''" ng-bind="::page.name"></span>
                <ul>
                    <li ng-repeat="subpage in ::page.children">
                        <a href="{{ ::subpage.path }}" ng-if="::subpage.path != ''"  title="{{ ::subpage.name }}" ng-bind="::subpage.name"></a>
                        <div ng-if="::subpage.children">
                            <span ng-if="::subpage.path == ''" ng-bind="::subpage.name"></span>
                            <ul>
                                <li ng-repeat="subsubpage in ::subpage.children">
                                    <a href="{{ ::subsubpage.path }}" title="{{ ::subsubpage.name }}" ng-bind="::subsubpage.name"></a>
                                </li>
                            </ul>
                        </div>
                    </li>
                </ul>
            </div>
        </li>
    </ul>
    

    Let me explain what is going on here…

    1. First we loop through sitemap.sitemap (remember we’re using sitemap with ControllerAs in sitemap route).
      <li ng-repeat="page in sitemap.sitemap">
      
    2. Now we test for a path. If we find one, we create a link to it (note that we are using the :: one time binding syntax here):
      <a href="{{ ::page.path }}" ng-if="::page.path != ''" title="{{ ::page.name }}" ng-bind="::page.name"></a>
      
    3. Then we test for children, if we find some we create a container:
      <div ng-if="::page.children">
      
    4. If we didn’t find a path earlier, now is the time to render the plain text:
      <span ng-if="::page.path == ''" ng-bind="::page.name"></span>
      
    5. Then we rinse and repeat for any nested page objects, and so on and so on…

yo-angular-sitemap

Add Sitemap to Sitemap

What good is a sitemap if it’s not one of the pages listed in your sitemap? Go into WordPress and find your sitemap-json page and add the sitemap. It should look something like this now:

[
{"name":"Home","path":"/","navigation":false},
{"name":"Test Page 1","path":"/test-page","navigation":true,"children":[
    {"name":"Sub Test 1","path":"/sub-test-1","navigation":true},
    {"name":"Sub Test 2","path":"/sub-test-2","navigation":true}
]},
{"name":"Test Page 2","path":"/test-page-2","navigation":true},
{"name":"Sitemap","path":"/sitemap","navigation":false}
]

Note that we’ve set the sitemap object’s navigation parameter to false. We don’t want the sitemap to appear in out top level navigation. Now when you refresh your sitemap page, you’ll see that our sitemap link has been added to the end of the list.

yo-angular-sitemap-in-sitemap

Add Sitemap to the Footer

In future posts we’ll make the footer dynamic, but for now we’ll just add a simple, static link to our sitemap.

  1. Open app/index.html.
  2. In the footer code, change this:
    <div class="footer">
      <div class="container">
        <p><span class="glyphicon glyphicon-heart"></span> from the Yeoman team</p>
      </div>
    </div>
    

    to this:

    <div class="footer">
      <div class="container">
        <p><a href="/sitemap" title="my app sitemap">Sitemap</a></p>
      </div>
    </div>
    

yo-angular-sitemap-in-footer

Summary

In this post we created a route for our sitemap, reused the sitemap object from a service we created in part 3, bound it to our sitemap view and added ways to get there with a link in the footer.

In future posts we’ll go over making a dynamic footer that contains all the links that you wouldn’t otherwise want in the navigation. Then I want to cover page specific configuration, site deployment and more.

Other Posts in this Series

 

Adam Merrifield

 

Leave a Reply