Comprehensive MEAN(Mongo, Express, Angular, and Nodejs) with Docker III

Angularjs is a very popular js framework backed by Google. So my front end site will base on Angularjs and adding other tools like bootstrap and ui-router.

The all four article is

  1. Environment setting - develop MEAN with docker.
  2. Nodejs and Express.
  3. *Angularjs.
  4. Continuous Development.

App architecture

Project Architecture

/app
|---/pages
    |---index.html
    |---index.js
    |---/about
    |   |---memoAbout.html
    |---/memo
        |---memoController.js
        |---memoPage.html
server.js
Dockerfile
docker-compose.yml
package.json
bower.json
Gulpfile.js

Bower.json

Bower is another popular package management tool for front end development. It works like npm.

{
  "name": "memoApp",
  "version": "0.0.0",
  "author": "Wei Ting Cheng <waitinduck@gmail.com>",
  "dependencies": {
    "angular": "^1.5.4",
    "angular-ui-router": "0.2.18",
    "bootstrap": "3.0.0"
  }
}

index.html

<!doctype html>

<!-- create your angularjs app -->
<html ng-app="memoApp">
<head>
    <!-- META -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1"><!-- Optimize mobile viewport -->

    <title>Node/Angular Todo App</title>
<!-- this is the place gulp will insert package like angularjs or bootstrap. I will explain more on this in the latter post-->
    <!-- inject:css -->
    <!-- endinject -->
    <!-- bowercss:css-->
    <!-- endinject -->
</head>
<!-- SET THE CONTROLLER AND GET ALL TODOS -->
<body>

<nav class="navbar navbar-inverse" role="navigation">
    <div class="navbar-header">
        <a class="navbar-brand" ui-sref="memo">MemoApp</a>
    </div>
    <ul class="nav navbar-nav">
        <li><a ui-sref="memo">Memo</a></li>
        <li><a ui-sref="about">About</a></li>
    </ul>
</nav>

<!-- will show the page in ui-view div-->
<div class="container">
    <div ui-view></div>
</div>

<!-- bowerjs:js-->
<!-- endinject -->
<!-- inject:js -->
<!-- endinject -->
</body>
</html>

One thing noted here. angularjs compile html file first. Which mean you need to specify ng-app, ng-controller in html, otherwise angularjs wont recognize it. I am using ui-router here so I could save sometime on write ng-controller.

index.js

angular.module('memoApp', ['ui.router'])

    .config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {

        $urlRouterProvider.otherwise("/");

        $stateProvider
            .state('memo', {
                url: '/',
                templateUrl: 'pages/memo/memoPage.html',
                controller: 'memoController'
            })

            // ABOUT PAGE 
            .state('about', {
                url: '/about',
                templateUrl: 'pages/about/memoAbout.html'
            });
    }]);

I use ui-router here. You can learn more about this tool in here. It will insert controller and template for you and allow you have nested route.

memoPage.html

<!-- HEADER AND TODO COUNT -->
<div class="jumbotron text-center">
    <h1>I'm a Memo <span class="label label-info">{{ todos.length }}</span></h1>
</div>

<!-- TODO LIST -->
<div id="todo-list" class="row">
    <div class="col-sm-4 col-sm-offset-4">

        <!-- LOOP OVER THE TODOS IN $scope.todos -->
        <div class="checkbox" ng-repeat="todo in todos">
            <label>
                <input type="checkbox" ng-click="deleteTodo(todo._id)"> {{ todo.text }}
            </label>
        </div>

    </div>
</div>

<!-- FORM TO CREATE TODOS -->
<div id="todo-form" class="row">
    <div class="col-sm-8 col-sm-offset-2 text-center">
        <form>
            <div class="form-group">

                <!-- BIND THIS VALUE TO formData.text IN ANGULAR -->
                <input type="text" class="form-control input-lg text-center" placeholder="I want to buy a puppy that will love me forever" ng-model="formData.text">
            </div>

            <!-- createToDo() WILL CREATE NEW TODOS -->
            <button type="submit" class="btn btn-primary btn-lg" ng-click="createTodo()">Add</button>
        </form>
    </div>
</div>

memoPageController.js

angular.module('memoApp')

    .controller('memoController', ['$scope', '$http',
        function($scope, $http) {
            $scope.formData = {};

            // when landing on the page, get all todos and show them
            $http.get('/todos')
                .success(function(data) {
                    $scope.todos = data;
                })
                .error(function(data) {
                    console.log('Error: ' + data);
                });

            // when submitting the add form, send the text to the node API
            $scope.createTodo = function() {
                $http.post('/todos', $scope.formData)
                    .success(function(data) {
                        $scope.formData = {}; // clear the form so our user is ready to enter another
                        $scope.todos = data;
                        console.log(data);
                    })
                    .error(function(data) {
                        console.log('Error: ' + data);
                    });
            };

            // delete a todo after checking it
            $scope.deleteTodo = function(id) {
                $http.delete('/todos/' + id)
                    .success(function(data) {
                        $scope.todos = data;
                        console.log(data);
                    })
                    .error(function(data) {
                        console.log('Error: ' + data);
                    });
            };
        }]);

Further Reading

You can find this code in my github repo MEAN pracitce.

  1. angularjs
  2. ui-router
  3. ui-bootstrap - a very good bootstrap plugin backed by angular team.

Prev: Nodejs and Express

Next: Continuous Development