Building the admin section for CRUD operations

We will now look to build the admin section of our CMS using Angular JS. The angularjs app will talk to the backend ExpressJS scripts that we just wrote in the preceding section.

Creating the routes for the admin section

Ideally, we would like our admin section to be called from within the admin URL, so let’s go ahead and add the routes for the admin section of the angularjs app.

Add the following routes to the angcms/public/js/app.js file:

config(['$routeProvider', '$locationProvider',
    function($routeProvider, $locationProvider) {

        $routeProvider.when('/admin/login', {
            templateUrl: 'partials/admin/login.html',
            controller: 'AdminLoginCtrl'
        });
        $routeProvider.when('/admin/pages', {
            templateUrl: 'partials/admin/pages.html',
            controller: 'AdminPagesCtrl'
        });
        $routeProvider.when('/admin/add-edit-page/:id', {
            templateUrl: 'partials/admin/add-edit-page.html',
            controller: 'AddEditPageCtrl'
        });
        $routeProvider.otherwise({
            redirectTo: '/'
        });
        $locationProvider.html5Mode(true);
    }
]);

For the admin side, we have three routes: /admin/login is to authenticate the user, /admin/pages will show the list of pages available, and /admin/add-edit-page/:id will be used to add or edit the contents of the page. Note that we will make use of a single route to both add and edit a page.

Building the factory services

As we are going to be reading the dynamic data from web services, we will create a factory service that will be used to communicate with the backend web service.

Let’s create our factory web services that will do the CRUD operations.

We will add the following methods to our angcms/public/js/services.js file:

'use strict';
angular.module('myApp.services', [])

.factory('pagesFactory', ['$http', 
  function($http) {

    return {
      getPages: function() {
        return $http.get('/api/pages');
      },

      savePage: function(pageData) {
        var id = pageData._id;

        if (id === 0) {
          return $http.post('/api/pages/add', pageData);
        } else {
          return $http.post('/api/pages/update', pageData);
        }
      },
      deletePage: function(id) {
        return $http.get('/api/pages/delete/' + id);
      },
      getAdminPageContent: function(id) {
        return $http.get('/api/pages/admin-details/' + id);
      },
      getPageContent: function(url) {
        return $http.get('/api/pages/details/' + url);
      },
    };
  }
]);

The methods to list, delete, and view the details of a page are quite straightforward; we simply make a request to the appropriate ExpressJS route that passes the id parameter where necessary.

Focusing on the savePage method, you’ll notice that we are using the same method to add a new page or edit the contents of an existing page. What we do here is we check for the id value in our post data. If the id value is set to 0, then it is treated as adding a new record; otherwise, it will try to update the record whose id value is being passed.

Building the controllers for the admin section

Now that we have our factory services ready, we’ll get started with writing our controllers.

We’ll add the following code to the angcms/public/js/controllers.js file:

'use strict';
angular.module('myApp.controllers', []).
controller('AdminPagesCtrl', ['$scope', '$log', 'pagesFactory',
  function($scope, $log, pagesFactory) {
    pagesFactory.getPages().then(
      function(response) {
        $scope.allPages = response.data;
      },
      function(err) {
        $log.error(err);
      });

      $scope.deletePage = function(id) {
        pagesFactory.deletePage(id);
      };

    }
]);

Tip

Don’t forget to delete the default controllers that come as a part of the angular-seed package.

The AdminPagesCtrl controller is primarily used to display the page’s listing.

We make a request to the getPages method of pagesFactory and populate the allPages scope object using the promise.

We also define our method to delete a page; the method accepts the id value as an input parameter.

Setting up the admin page layout

We’ll now work on building our listing view that will display a list of all the pages, along with the ability to add, edit, or delete a page.

Before we get to our listing view, let’s first get the groundwork ready on our Index page located at angcms/public/index.html.

Ensure that your index.html file contains the following code:

<!doctype html>
<html lang="en" ng-app="myApp">

<head>
    <meta charset="utf-8">
    <title>Angular CMS</title>
     <base href="/">
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap-theme.min.css" />
    <link rel="stylesheet" href="css/app.css" />
</head>

<body>
    <div class="container" ng-view></div>
    <script src="bower_components/angular/angular.js"></script>
    <script src="bower_components/angular-route/angular-route.js"></script>
   <script src="js/app.js"></script>
    <script src="js/services.js"></script>
    <script src="js/controllers.js"></script>
    <script src="js/filters.js"></script>
    <script src="js/directives.js"></script>
</body>

</html>

We will leverage BootStrap3 to get our styling in place. You can choose to either download Bootstrap from www.getbootstrap.com, call it from any of the CDN, or run the following command in the terminal from within the angcms/public folder:

bower install bootstrap

As you can see from the code, we are loading bootstrap and Bootstrap-theme CSS files to take advantage of the default Bootstrap theme.

The only other change to the index.html file at this stage is adding the container CSS class to our ng-view div. This will act as the container for all the pages that load within it.

Building the listing view for the admin section

Next, we’ll create the partial that will display our list of pages stored in the database.

Create a folder named admin and a new file named pages.html at angcms/public/partials/admin/pages.html, and add the following code:

<a href="#/admin/add-edit-page/0" class="btn btn-success pull-right"> Add New Page</a>
<h1>Pages List</h1>
<hr/>
<table class="table">
  <thead>
    <tr>
      <th>Menu Index</th>
      <th>Title</th>
      <th>URL</th>
      <th>Edit</th>
      <th>Delete</th>
    </tr>
  </thead>

  <tr ng-repeat="page in allPages">
    <td>{{page.menuIndex}}</td>
    <td>{{page.title}}</td>
    <td>{{page.url}}</td>
    <td> <a ng-href="#/admin/add-edit-page/{{page._id}}">Edit</a>
    </td>
    <td> <a ng-href="#" ng-click="deletePage(page._id)">Delete</a>
    </td>
  </tr>
</table>

At the top, we have a button to add new pages. It will link to the add-edit-page route and pass a fixed ID of 0. As you might have realized, we are reusing our partial to add and edit the page. We will need to let angularjs know when to call the add endpoint and when to call the edit endpoint. For this reason, we pass 0 as a parameter while adding a new page and the MongoDB-assigned ID while editing a page.

The next piece of code is the table to display our list of pages with the title and URL fields. Along with it, we also have links to edit or delete the respective page. Both these hyperlinks link to the respective routes that pass the page ID.

Save the file and point the browser URL to http://localhost:3000/admin/pages . This should show you a list of pages. In case you don’t see any pages, check for any console errors or add some content using a REST Client for the time being, until our add-edit-page route is ready.

The delete link will not work for now as its API is authenticated.