Create simple TODO app AngularJS with bootstrap and Spring MVC

In this postwe will show how to create simple TODO application which consists of two main parts.
1. AngularJS GUI client
2. Spring MVC war file as backend for this application

Firstly, we will consider GUI client build on AndroidJS framework.
In Front-end we consider two views and two controllers. First view and controller is for manipulating with login logic and second for showing and retrieving data from server (our backend).
We will use bootstrap framework as our base style, because we want to create responsive design with idea „mobile-first“. Keep in mind that we are not focusing on great design.

So create index and enter page in our TODO app:

 
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>AngularJS demcak todo</title>
    <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular-route.js"></script>   
 
    <script src="demcak-app.js"></script>
    <style type="text/css">
    body{
        font-family: Verdana, Arial, Helvetica, sans-serif;
    }
    .row:nth-of-type(even) {
        background: #e0e0e0;
    }
    .d_margin{
         padding-left:50px;
         padding-right:50px;
    }
    .wrapper {
      height: 700px;
      overflow: auto;
    }
    .box1 {
      background-color: #f9f9f9;
    }
    .box1  input{
     width: "100%";
    }
    .box2 {
      background-color: #f9f9f9;
    }
    .box3 {
      background-color: #f9f9f9;
    }
    .larger-font-size{
        font-size: larger;
    }
    a{
        cursor: pointer;
    }
    input[type=text], input[type=date], textarea { width: 100%; box-sizing: border-box; height: 28px; }
    textarea {
        min-height: 10%;
        resize: none;
    }
    </style>
  </head>
  <body ng-app="demcakTODOapp">
    <div ng-view>
    </div> 
  </body>
</html>

Now we need to create controllers. Our two views are containers which are controlled by different cntrollers.

1 view (login.html)

    <div class="container"  ng-controller="LoginController as loginCtrl">
      <form class="login-form" ng-submit="loginCtrl.postForm()">
        <h2>Demcak Todo app Login</h2>
        <input type="text" class="form-control" placeholder="Username" required autofocus ng-model="loginCtrl.inputData.username">
        <input type="password" class="form-control" placeholder="Password" required ng-model="loginCtrl.inputData.password">
		<br>
		<div class="alert alert-danger" role="alert" ng-show="errorMsg">{{errorMsg}}</div>
        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
      </form>
    </div>

2 view (todo.html)

 
<div class="container" ng-controller="net-demcak-TODO-controller">
    <div class="row">
        <h1>Todo demcak</h1>
    </div>
    <div class="row ">
        <div class="col-sm-4 col-md-3 col-xs-4">
 
        </div>
    </div>
    <div class="row d_margin">
 
    <!-- BEGIN user input -->
        <div class="col-sm-4 col-md-4 col-xs-12 wrapper box1">
        <h2>User info</h2>
        <img  class="img-responsive" src="http://simpleicon.com/wp-content/uploads/user1.png">
         <p>Filter by title TODO <input type="text" ng-model="filterByTitle"></p> 
         <h2>Create new task</h2>
        <div class="input-block-level">
        <form>
            <input type="text" ng-model="newTodo.title" placeholder="What" maxlength="35">
            <input type="date" ng-model="newTodo.date">
            <textarea ng-model="newTodo.description" placeholder="Description"></textarea>
 
            <button ng-click="addNewTodo()">Add new Task</button>
        </form>
       </div>
        <!-- TODO OR BETWEEN <input type="date" ng-model="from"> AND <input type="date" ng-model="to">-->
        </div>
        <div class="col-sm-4 col-md-4 col-xs-12 wrapper box2 larger-font-size">
            <h2>Tasks unfin - {{(todosList|filter:{archive:false}).length}}</h2>
            <div class="row" ng-repeat="actTodo in todosList | orderBy: '-date':true | filter:filterByTitle | filter:{archive:false}">
                <h3><a ng-click="setSelectedTask(actTodo)">{{actTodo.title}}</a></h3>
                <div class="col-sm-9 col-md-9 col-xs-9">{{actTodo.date}}</div>
                <div class="col-sm-3 col-md-3 col-xs-3">
                    <button type="submit" style="border: 0; background: transparent" ng-click="makeDone(actTodo)" >
                        <img src="OK.png" alt="submit" />
                    </button>
                </div>
            </div>
        </div>
        <div class="col-sm-4 col-md-4 col-xs-12 wrapper box3 larger-font-size">
            <h2>Archive</h2>  
                <div class="row" ng-repeat="actTodo in todosList | orderBy: '-date':true | filter:filterByTitle | filter:{archive:true}">
                <div class="col-sm-6 col-md-6 col-xs-6">{{actTodo.title}}</div>
                <div class="col-sm-3 col-md-3 col-xs-3">{{actTodo.date}}</div>
            </div>
        </div>
    </div>
    <div class="row">
        <div class="col-sm-4 col-md-3 col-xs-4">
             <div class="row "><h3>Task - {{selectedTodoTask.title}}</h3></div>  
             <div class="row "><p>Date - {{selectedTodoTask.date}}</p></div>   
             <div class="row "><p>Description - {{selectedTodoTask.description}}</p></div>  
        </div>
    </div>
</div>

And full app.js (angularjs module with controllers)

var demcakTODOapp = angular.module('demcakTODOapp', ['ngRoute']);
 
demcakTODOapp.config(function($routeProvider) {
    $routeProvider
    // route for the home page
        .when('/', {
        templateUrl: 'login.html',
        controller: 'LoginController'
    })
 
    .when('/todo', {
        templateUrl: 'todo.html',
        controller: 'net-demcak-TODO-controller'
    });
});
 
 
demcakTODOapp.controller('net-demcak-TODO-controller', function($route, $scope, $http, sharedActualUser) {
    $scope.user_id = ''
    $scope.user = '';
    $scope.newTodo = '';
 
    $scope.todosList = [ /*{title:'title',date:'date'}*/ ];
 
    $scope.selectedTodoTask = '';
 
    init();
 
    $scope.setSelectedTask = function(actTodoTask) {
        $scope.selectedTodoTask = actTodoTask;
    }
 
    $scope.addNewTodo = function() {
        var newTD;
        if ($scope.newTodo.title == null) {
            return;
        }
        console.log('added new todo: ' + $scope.newTodo);
        if ($scope.newTodo.date == null) {
            $scope.newTodo.date = 'someday'
        }
        $scope.newTodo.fk_uid = sharedActualUser.getActualUser();
 
 
        var req = {
            method: 'POST',
            url: '/demcak_todo_backend-1/add',
            headers: {
                'Content-Type': 'application/json'
            },
            data: JSON.stringify($scope.newTodo)
 
        }
 
        $http(req).success(function(data) {
            console.log(data);
            init();
        }).error(function() {
            console.log("calling url failed");
            return "failed";
        });
 
    }
 
    $scope.makeDone = function(actTodo) {
 
        console.log('added new todo: ' + actTodo.title);
 
        var req = {
            method: 'PUT',
            url: '/demcak_todo_backend-1/put?u_id=' + actTodo.fk_uid + '&todo_id=' + actTodo.id,
            headers: {
                'Content-Type': 'application/json'
            }
        }
 
        $http(req).success(function(data) {
            console.log(data);
            init();
        }).error(function() {
            console.log("calling url failed");
            return "failed";
        });
    }
 
    function init() {
 
        var req = {
            method: 'GET',
            url: '/demcak_todo_backend-1/get?id=' + sharedActualUser.getActualUser(),
            headers: {
                'Content-Type': 'application/json'
            }
        }
 
        $http(req).success(function(data) {
            console.log(data);
            $scope.todosList = data;
        }).error(function() {
            console.log("calling url failed");
            return "failed";
        });
    }
 
});
 
 
demcakTODOapp.controller('LoginController', function($route, $scope, $http, sharedActualUser, $location) {
 
 
    this.postForm = function() {
 
        var encodedString = 'username=' + encodeURIComponent(this.inputData.username);
 
        if (encodeURIComponent(this.inputData.password) != "test") {
            alert('wrong password');
            return;
        }
 
        $http({
                method: 'GET',
                url: '/demcak_todo_backend-1/login?' + encodedString,
                data: encodedString,
                headers: {
                    'Content-Type': 'application/json'
                }
            })
            .success(function(data, status, headers, config) {
                console.log("data userId" + data.userId);
                sharedActualUser.setActualUser(data.userId);
                $location.path('todo');
            })
            .error(function(data, status, headers, config) {
                $scope.errorMsg = 'Error while loggining';
            })
    }
 
});
 
 
demcakTODOapp.factory('sharedActualUser', function() {
    var actualUser = '';
 
    return {
        getActualUser: function() {
            return actualUser;
        },
        setActualUser: function(a) {
            actualUser = a;
        }
    };
});

And controllers in app.js

App.js with controllers.

Back-end is built on Spring MVC and provides REST API for adding, updating and getting data. We should use any DBMS but for simplicity we will consider only memory-based database.
We need to create two RestControllers for controlling http request from client.

1. Controller

package net.demcak.dump;
 
import net.demcak.dump.model.TodoEntity;
import net.demcak.dump.services.DatabaseService;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
 
import java.util.LinkedList;
import java.util.List;
 
/**
 * Controller for manipulation with task entity
 *
 * @author  Vlado Demcak
 * @version 1.0
 */
@RestController
@RequestMapping("/")
public class RESTTaskController {
 
    /**
     * This method returns list of tasks for user (his id)
     * @param id
     * @return ResponseEntity status of adding task.
     */
    @RequestMapping(value = "/get", method = RequestMethod.GET)
    public
    @ResponseBody
    List getTodoListForUserId(@RequestParam("id") int id) {
        List userTodoList = (List) DatabaseService.getInstance().getDb().get(id);
        return userTodoList;
    }
 
    /**
     * This method adds new task in "database".
     * @param newTodo newTodo.
     * @return ResponseEntity status of adding task.
     */
    @RequestMapping(value = "/add", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Object> createTodoForUserId(@RequestBody TodoEntity newTodo) {
        int id;
        try {
            id = Integer.valueOf(newTodo.getFK_uid());
        } catch (NumberFormatException e) {
            return new ResponseEntity<>(HttpStatus.FORBIDDEN);
        }
 
        List userTodoList = (List) DatabaseService.getInstance().getDb().get(id);
 
        if (userTodoList == null) {
            DatabaseService.getInstance().getDb().put(id, new LinkedList<TodoEntity>());
            userTodoList = (List) DatabaseService.getInstance().getDb().get(id);
        }
 
        newTodo.setId(userTodoList.size());
        userTodoList.add(newTodo);
        DatabaseService.getInstance().getDb().put(id, userTodoList);
        return new ResponseEntity<>(HttpStatus.OK);
    }
 
    /**
     * This method edits task. We need id of user and id of task (because we dont have global list of task)".
     * @param u_id user id.
     *  @param todo_id todo task id position in list.
     * @return ResponseEntity status of mark task done.
     */
    @RequestMapping(value = "/put", method = RequestMethod.PUT)
    public
    @ResponseBody
    ResponseEntity<Object> makeTodoDoneByTodoID(@RequestParam("u_id") int u_id, @RequestParam("todo_id") int todo_id) {
        int id;
        try {
            id = Integer.valueOf(u_id);
        } catch (NumberFormatException e) {
            return new ResponseEntity<>(HttpStatus.FORBIDDEN);
        }
 
        List userTodoList = (List) DatabaseService.getInstance().getDb().get(id);
 
        if (userTodoList == null) {
            DatabaseService.getInstance().getDb().put(id, new LinkedList<TodoEntity>());
            userTodoList = (List) DatabaseService.getInstance().getDb().get(id);
        }
 
        ((TodoEntity) userTodoList.get(todo_id)).setArchive(true);
        DatabaseService.getInstance().getDb().put(id, userTodoList);
        return new ResponseEntity<>(HttpStatus.OK);
    }
}

2. Controller

package net.demcak.dump;
 
import net.demcak.dump.model.TodoEntity;
import net.demcak.dump.model.User;
import net.demcak.dump.services.DatabaseService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
 
/**
 * Controller for manipulation with login
 *
 * @author  Vlado Demcak
 * @version 1.0
 */
@RestController
@RequestMapping("/")
public class RESTUserController {
 
 
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public
    @ResponseBody
    HashMap loginCheck(@RequestParam("username") String username) {
 
        HashMap response = new HashMap<String, Object>();
        response.put("new_registration", "false");
 
        int userId = selectUserFromDbByUsername(username);
        if (userId == -1) {
            userId = createNewUser(username);
            response.put("new_registration", "true");
            DatabaseService.getInstance().getDb();
        }
 
 
        response.put("status", "ok");
        response.put("userId", userId);
        return response;
    }
 
    private int createNewUser(String username) {
            User u = new User();
            u.setLogin(username);
            return DatabaseService.getInstance().createUser(u);
    }
 
    private int selectUserFromDbByUsername(String username) {
        ArrayList<User> listUsers = DatabaseService.getInstance().getUsers();
        for(User un : listUsers){
            if(un.getLogin().equalsIgnoreCase(username)){
                return un.getId();
            }
        }
        return -1;
    }
}

How it looks like:
AngularJS GUI login
AngularJS GUI TODO APP

You can download source code on this link.