Building A Content Management System Using The MEAN Stack - 6 (Front-End Development)

in #utopian-io6 years ago

Repository

https://github.com/nodejs/node

What Will I Learn

The codebase for this tutorial is based on MEANie an open source content management system by Jason Watmore.

This is the fifth tutorial in the series of tutorials on building a content management system using the MEAN technology.

In the earlier tutorials we worked on all the components needed for the backend to run including the server, controllers and services.

Additionally we started working on the client side of our application particularly the admin area.

In the last tutorial we created the posts view for the admin area.

In this tutorial we will work on the pages, redirects and account view for our cms.

By the end of this tutorial we will have completed work on some of the features for the admin section of the application.

Mainly we will create a user interface to display the list of pages and redirects from the admin dashboard in the application.

We will also create forms to be used in creating and editing new posts, pages, redirects and user account details.

We will add directives and functions to make the layout functional and communicate with the API.

N.B;- LINK TO THE EARLIER TUTORIALS IN THIS SERIES CAN BE FOUND AT THE END OF THIS POST

Requirements

Difficulty

  • Intermediate

Tutorial Contents

Pages

Let's go and create a new directory pages in the admin directory.

To create a view template for the list of pages stored in the database we'll first of all add a new file index.view.html.

In this file we'll have the following code

<div  class="posts-area">
    <div  class="posts-area-header row">
        <div  class="col l6 m6">
            <h5>Pages</h5>
        </div>
        <div  class="col l6 m6 new-post-button">
            <a  href="#/pages/add"  class="waves-effect waves-light btn right indigo lighten-2 add"><i  class="icon ion-ios-add"></i></a>
        </div>
    </div>
    <div  class="row">
        <div  class="col l6 m6"  ng-repeat="page in vm.pages">
            <div  class="card">
                <div  class="card-content">
                    <p  class="card-title">Page Title: <br>  <span  class="center">{{page.title}}</span></p>
                    <p>Published <i  ng-if="page.publish"  class="icon ion-md-checkmark checkmark right" /></p>
                </div>
                <div  class="card-action">
                    <a  href="#/pages/edit/{{page._id}}"  class="btn indigo lighten-2 edit-btn"><i  class="icon ion-md-create"></i></a>
                </div>
            </div>
        </div>
    </div>
</div>

In the pages view template all pages and accompanying details are displayed in a card.

In the card we have a paragraph displaying the page title bounded by {{page.title}}.

The card action area holds a button that allows the admin edit the content of each page.

To make the template functional we need to add the controller file.

Create a new file in the pages directory index.controller.js . Inthe controller file add the following code

(function () {
    'use strict';
    angular
        .module('app')
        .controller('Pages.IndexController', Controller);

    function  Controller(PageService) {
        var  vm  =  this;
        vm.pages  = [];

        initController();

        function  initController() {
            vm.loading  =  true;
            PageService.GetAll()
                .then(function (pages) {
                vm.loading  =  false;
                vm.pages  =  pages;
                });
        }
    }

})();

In this controller file initController() goes to the PageService module and runs the GetAll() function which returns a list of all pages stored in the database and stores them as an array in vm.pages.

Intside the admin directory there is a services directory which was created in an earlier tutorial, in there create a page.service.js file.

Paste the following code in that file\

(function () {

    'use strict';

    angular
        .module('app')
        .factory('PageService', Service);  

    function  Service(DataService) {
        var  service  =  DataService('/api/pages');
        return  service;
    }

})();

The PageService module grabs all the date from the api/pages directory and serves it to the pages controller module in the client directory.

Below is a screenshot of the pages view.

We also need a add-edit view to handle the addition and editing of page contents.

In the pages directory add a new file add-edit.view.html. In this file paste the following code

<div  class="add-post-area container">
    <h5>Page Details</h5>
    <form  ng-if="!vm.loading">
        <div  class="input-field">
            <input  placeholder="Title"  id="title"  type="text"  ng-model="vm.page.title" />
        </div>
        <div  class="input-field">
            <input  placeholder="Slug"  type="text"  id="slug"  ng-model="vm.page.slug" />
            <a  class="generate-slug btn waves waves-effect indigo lighten-2"  ng-click="vm.page.slug = (vm.page.title | slugify)">Generate from title</a>
        </div>
        <div  class="input-field">
            <textarea  placeholder="Summary"  id="summary"  ng-model="vm.page.description"  rows="4"></textarea>
        </div>
        <div  class="input-field">
            <textarea  ng-model="vm.page.body"  wysiwyg></textarea>
        </div>
        <p>
            <label>
                <input  type="checkbox"  ng-model="vm.page.publish" />
                <span>Publish</span>
            </label>    
        </p>
        <div  class="center">
            <a  class="btn waves waves-effect indigo lighten-2"  ng-click="vm.savePage()">SAVE</a>  
            <a  class="btn waves waves-effect indigo lighten-2"  href="#/pages">CANCEL</a>
            <a  class="btn waves waves-effect indigo lighten-2"  ng-click="vm.deletePage()"  ng-show="vm.page._id">DELETE</a>
        </div>
    </form>
</div>

In the file created above we have a form with several input fields with each input having its own ng-model directive.

The field accepting the page title has the ng-model="vm.page.title" .

For the page slug we have the ng-model="vm.page.slug".

Page summary has ng-model="vm.page.description".

Page body has ng-model="vm.page.body".

We also add three buttons at the end.

The first button will handle saving the added page contents to the database. That operation is indicated through the included directive ng-click="vm.savePage()".

The second button will cancel any ongoing process in the page edit area and return the user to the index.view.html front end for the pages view.

The third button is responsible for deleting an existing page from the database and contains two included directives ng-click="vm.deletePage()" and ng-show="vm.page._id"

ng-click="vm.deletePage()" handles the page deletion aspect while ng-show="vm.page._id" will make sure that the button only displays if the page being edited was already existing on the database before that time.

To add the controller for the add-edit template we need to create a new file add-edit.controller.js.

The code for the add-edit controller

(function () {
    'use strict';

    angular
        .module('app')
        .controller('Pages.AddEditController', Controller);

    function  Controller($stateParams, $location, $filter, PageService, AlertService) {
        var  vm  =  this;
        
        vm.page  = {};
        vm.savePage  =  savePage;
        vm.deletePage  =  deletePage;

        initController();

        function  initController() {
            vm.loading  =  0;

            if ($stateParams._id) {
                vm.loading  +=  1;
                PageService.GetById($stateParams._id)
                    .then(function (page) {
                        vm.loading  -=  1;
                        vm.page  =  page;
                    });
            } else {
                // initialise with defaults
                vm.page  = {
                    publish:  true
                };
            }
        }

        function  savePage() {
            PageService.Save(vm.page)
                .then(function () {
                    AlertService.Success('Page saved', true);
                    $location.path('/pages');
                })
                .catch(function (error) {
                    AlertService.Error(error);
                });
        }
  
        function  deletePage() {
            PageService.Delete(vm.page._id)
                .then(function () {
                    AlertService.Success('Page deleted', true);
                    $location.path('/pages');
                })
                .catch(function (error) {
                    AlertService.Error(error);
                });
        }
    }
  
})();

savePage() will grab all the data provided in the form and saves them in the vm.page array initialized earlier in the file.

The values in vm.page are then sent to the /pages api in the server directory to be processed and stored in the database.

deletePage() will delete the page matching the id provided by the vm.page._id variable.

Screenshot of the add-edit view after completion.


Redirects

We are going to create a view for the redirect module, in order to do that we are going to create a new directory in the admin directory and name it redirects.

In the redirects directory we'll create a new file index.view.html and add the following code

<div  class="posts-area">
    <div  class="posts-area-header row">
        <div  class="col l6 m6">
            <h5>301 Redirects</h5>
        </div>
        <div  class="col l6 m6 new-post-button">
            <a  href="#/redirects/add"  class="waves-effect waves-light btn right indigo lighten-2 add">
                <i  class="icon ion-ios-add"></i>
            </a>
        </div>
    </div>
    <div  class="row">
        <div  class="col l6 m6"  ng-repeat="redirect in vm.redirects">
            <div  class="card">
                <div  class="card-content">
                    <p>Redirect From: <br>  <span  class="center">{{redirect.from}}</span></p>
                    <p>Redirect To: <br>  <span  class="center">{{redirect.to}}</span></p>
                </div>
                <div  class="card-action">
                    <a  href="#/redirects/edit/{{redirect._id}}"  class="btn indigo lighten-2 edit-btn"><i  class="icon ion-md-create"></i></a>
                </div>
            </div>
        </div>
    </div>
</div>

The redirect template is rendered through a column with a directive ng-repeat="redirect in vm.redirects" which translates to for every redirect in vm.redirects repeat the following.

Each redirect is displayed in a card with the following details

  • {{redirect.from}} outputs the page the redirect is coming from.
  • {{redirect.to}} outputs the page being redirected to.

And also we have a button in the card action section that allows the user to edit an existing redirect.

The link in the card action area points to href="#/redirects/edit/{{redirect._id}}".

The redirect controller will be contained in the index.controller.js file.

Code for the redirect controller

(function () {
    'use strict';

    angular
        .module('app')
        .controller('Redirects.IndexController', Controller);
  
        function  Controller(RedirectService) {
            var  vm  =  this;
            vm.redirects  = [];
            
            initController();
  
             function  initController() {
                vm.loading  =  true;
                RedirectService.GetAll()
                    .then(function (redirects) {
                        vm.loading  =  false;
                        vm.redirects  =  redirects;
                    });
            }
        }
        
})();

In the redirect controller the Controller() method grabs all the redirects stored in the database through the RedirectService module and stores them as an array in vm.redirects.

The service file for the redirect view is stored in the services directory inside the admin directory.

The name for this file redirect.service.js. The code or the RedirectService module

(function () {
    'use strict';

    angular
        .module('app')
        .factory('RedirectService', Service);

        function  Service(DataService) {
            var  service  =  DataService('/api/redirects');
            return  service;
        }
        
})();

The function Service() pulls all needed data from /api/redirects and stores all the values gotten in service for them to be used by the redirect controller.

Below is a screenshot of the redirect html template

The template file for the add-edit view of the redirect add-edit.view.html would contain the following code

<div  class="add-edit-form-area container">
    <h5>301 Redirect Details</h5>
    <form  ng-if="!vm.loading">
        <div  class="input-field">
            <input  placeholder="From (e.g. '/old-url')"  id="from"  type="text"  ng-model="vm.redirect.from"  required />
        </div>
        <div  class="input-field">
            <input  placeholder="To (e.g. '/new-url')"  type="text"  id="to"  ng-model="vm.redirect.to" />
        </div>
        <div  class="center">
            <a  class="btn waves waves-effect indigo lighten-2"  ng-click="vm.saveRedirect()">SAVE</a>
            <a  class="btn waves waves-effect indigo lighten-2"  href="#/redirects">CANCEL</a>
            <a  class="btn waves waves-effect indigo lighten-2"  ng-click="vm.deleteRedirect()"  ng-show="vm.redirect._id">DELETE</a>
        </div>
    </form>
</div>

The form has two input fields, the first input field with ng-model="vm.redirect.from" will accept the URL of the page the redirect is coming form as input.

The second input field with ng-model="vm.redirect.to" will accept the URL of the page being redirected to as input.

To add the controller for the add-edit template create a new file add-edit.controller.js

The code for the add-edit.controller.js

(function () {
    'use strict';

     angular
        .module('app')
        .controller('Redirects.AddEditController', Controller);
  
    function  Controller($stateParams, $location, $filter, RedirectService, AlertService) {
        var  vm  =  this;

        vm.redirect  = {};
        vm.saveRedirect  =  saveRedirect;
        vm.deleteRedirect  =  deleteRedirect;

        initController();

        function  initController() {
            vm.loading  =  0;
            
            if ($stateParams._id) {
                vm.loading  +=  1;
                RedirectService.GetById($stateParams._id)
                    .then(function (redirect) {
                        vm.loading  -=  1;
                        vm.redirect  =  redirect;
                    });
            }
        }

        function  saveRedirect() {
            RedirectService.Save(vm.redirect)
                .then(function () {
                    AlertService.Success('Redirect saved', true);
                    $location.path('/redirects');
            })
            .catch(function (error) {
                AlertService.Error(error);
            });
        }

        function  deleteRedirect() {    
            RedirectService.Delete(vm.redirect._id)
                .then(function () {
                    AlertService.Success('Redirect deleted', true);
                    $location.path('/redirects');
                })
                .catch(function (error) {
                    AlertService.Error(error);
                });
        }
    }
    
})();

The method Controller() has three functions.

initController() initializes all available redirect provided by the RedirectService() module for them to be operated on by other functions in the redirect controller.

saveRedirect() will save a new redirect added from the html template through the RedirectService() .

deleteRedirect() will delete a new redirect added from the html template through the RedirectService().

Screenshot of the add-edit view

Account Update

This view will allow users to edit and update their account details from the admin front end.

The code for this view is contained in the account directory created in the admin directory.

Inside the account directory we have the html template in the index.view.html file

<div  class="add-edit-form-area container">
    <h5>My Account</h5>
    <form  method="post">
        <div  class="input-field">
            <input  placeholder="Username"  id="username"  type="text"  ng-model="vm.user.username"  required />
        </div>
        <div  class="input-field">
            <input  placeholder="Password"  type="text"  id="password"  ng-model="vm.user.password" />
        </div>
        <div  class="center">
            <a  class="btn waves waves-effect indigo lighten-2"  ng-click="vm.saveUser()">UPDATE</a>
        </div>
    </form>
</div>

The form above has two input fields, the first field accepts the new username to be updated in the database with the ng-model="vm.user.username".

The second field accepts the new password to be updated in the database with the ng-model="vm.user.password".

We also have a button with the ng-click="vm.saveUser()" directive which handles saving the updated user data to the database.

The accounts controller is contained in the index.controller.js file created in the accounts directory.

Code for the accounts controller

(function () {
    'use strict';

    angular
        .module('app')
        .controller('Account.IndexController', Controller);

    function  Controller($window, UserService, AlertService) {
        var  vm  =  this;

        vm.user  =  null;
        vm.saveUser  =  saveUser;
        vm.deleteUser  =  deleteUser;
  
        initController();
        
        function  initController() {
            // get current user
            UserService.GetCurrent().then(function (user) {
                vm.user  =  user;
            });
        }

        function  saveUser() {
            UserService.Update(vm.user)
                .then(function () {
                    AlertService.Success('User updated');
                })
                .catch(function (error) {
                    AlertService.Error(error);
                });
        }

        function  deleteUser() {
            UserService.Delete(vm.user._id)
                .then(function () {
                    // log user out
                    $window.location  =  '/login';
                })
                .catch(function (error) {
                    AlertService.Error(error);
                });
        }
    }

})();

In the account controller we have the Controller() method which is the main function and contains three functions.

initController() gets the current user from the database through the current UserService module.

saveUser() replaces/updates the user details in the database with the details provided in the html template form through the UserService module.

deleteUser() deletes the current user from the database through the UserSevice module.

UserService is located in the services directory in the user.service.js files

The following is the code for the user.service.js file

(function () {
    'use strict';

    angular
        .module('app')
        .factory('UserService', Service);

    function  Service($http, $q) {
        var  service  = {};
  
        service.GetCurrent  =  GetCurrent;
        service.Update  =  Update;
        return  service;

        function  GetCurrent() {
            return  $http.get('/api/users/current').then(handleSuccess, handleError);
        }

        function  Update(user) {
            return  $http.put('/api/users/'  +  user._id, user).then(handleSuccess, handleError);
        }

        // private functions

        function  handleSuccess(res) {
            return  res.data;
        }

        function  handleError(res) {
            return  $q.reject(res.data);
        }
    }

})();

The main function in user.service.js file is Service() and it contains four other functions

GetCurrent() runs an GET request and returns the details of the current user.

Update() runs a POST request to update the user details in the database with the new user data provided.

handleSuccess() runs if either of the two functions above is successfully and it returns the data request in either functions.

handleError() runs if either of the two functions above runs into an error during execution.

Screenshot of the Account details update template

In the next tutorial we will work on other service modules required for the admin section. Also we will create the directives and filters needed for this module.

Curriculum

  1. Building A Content Management System Using The MEAN Stack - 1 (Create Server, Config File and Helper Modules)

  2. Building A Content Management System Using The MEAN Stack - 2(Create Controller Modules 1)

  3. Building A Content Management System Using The MEAN Stack - 3 (Create Controller Modules 2)

  4. Building A Content Management System Using The MEAN Stack - 4 (Create Services Modules)

  5. Building A Content Management System Using The MEAN Stack - 5 (Front-End Development)

Proof Of Work Done

https://github.com/olatundeee/mean-cms

Sort:  

I thank you for your contribution. Here is my thought;

  • What you really have shown is doing it by yourself, not teaching. You can avoid this by approaching more theoretical/technical to the subject. If you need examples, there are sites which offer professional tutorials. I won't include them because I don't want to advertise them, but checking them might change your approach to the tutorials and increase the value of your post.

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]

Thank you for your review, @yokunjon!

So far this week you've reviewed 1 contributions. Keep up the good work!

As a follower of @followforupvotes this post has been randomly selected and upvoted! Enjoy your upvote and have a great day!

Hey, @gotgame!

Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Get higher incentives and support Utopian.io!
Simply set @utopian.pay as a 5% (or higher) payout beneficiary on your contribution post (via SteemPlus or Steeditor).

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Coin Marketplace

STEEM 0.19
TRX 0.15
JST 0.029
BTC 63138.55
ETH 2579.36
USDT 1.00
SBD 2.80