Een Todo application with Spring REST and AngularJS

In this web application we will use Angular as frontend and Spring Data (CRUD Repository) as the backend

   todo 
     + src
     | + main
     | | + java 
     | |   + com
     | |     + litteworld
     | |       + web
     | |         + model
     | |            - Todo.java (Entity)
     | |         + controller
     | |            - TodoController.java (Spring MVC REST)
     | |         + services
     | |            - TodoService.java (CRUDRepository)
     | + webapp
     |   + resources
     |     - todo.js
     |   + WEB-INF
     |     + views
     |       - todo.jsp
     |     - web.xml 
     |     - spring-context.xml 
     - pom.xml

Maven POM

In the POM we define the library dependencies of this project

We will use the jetty plugin


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.littleworld</groupId>
  <artifactId>todo</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>todo</name>

  <properties>
    <jdk.version>1.8</jdk.version>
    <jstl.version>1.2</jstl.version>
    <servlet.version>3.1.0</servlet.version>
    <springframework.version>4.2.3.RELEASE</springframework.version>
    <hibernate.version>4.3.6.Final</hibernate.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-jpa</artifactId>
      <version>1.9.1.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>${hibernate.version}</version>
    </dependency>
   
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.37</version>
    </dependency>

    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.1.3</version>
    </dependency>

    <dependency>
    	<groupId>com.fasterxml.jackson.core</groupId>
    	<artifactId>jackson-databind</artifactId>
    	<version>2.6.3</version>
    </dependency>
  </dependencies>


  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.3</version>
        <configuration>
          <source>${jdk.version}</source>
          <target>${jdk.version}</target>
        </configuration>
      </plugin>

      <plugin>
      <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>9.3.6.v20151106</version>
        <configuration>
          <scanIntervalSeconds>10</scanIntervalSeconds>
          <webApp>
            <contextPath>/todo</contextPath>
          </webApp>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-eclipse-plugin</artifactId>
        <version>2.9</version>
        <configuration>
          <wtpversion>2.0</wtpversion>
          <wtpContextName>todo</wtpContextName>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Model

This project uses the todo as model

The fields in the model will be used in the views form and list. Apart from the id field. The model is mapped to a database table The id is autogenerated


/* generated by: ControllerGenerator Tue Dec 06 14:16:47 CET 2016 */
package com.littleworld.todo.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Todo {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  int id;

  String task;

  public Todo() {}

  public Todo(int id, String task) {
    this.id = id;
    this.task = task;  }
  public int getId() { return id; }
  public void setId(int id)  { this.id = id; } 
  public String getTask() { return task; }
  public void setTask(String task)  { this.task = task; }  
  @Override 
  public String toString() {
    return "[Todo: [" + "id: " + id + ", " + "task: " + task + "]"; 
  }
}

Context

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:mvc="http://www.springframework.org/schema/mvc"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns="http://www.springframework.org/schema/beans"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:jpa="http://www.springframework.org/schema/data/jpa"
  xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

  <mvc:annotation-driven />
  <mvc:resources mapping="/resources/**" location="/resources/" />

  <bean
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
  </bean>

  <bean name="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/todo" />
    <property name="username" value="root" />
  </bean>

  <bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="packagesToScan" value="com.littleworld.todo.model" />
      <property name="jpaVendorAdapter">
         <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
      </property>
      <property name="jpaProperties">
         <props>
            <prop key="hibernate.hbm2ddl.auto">create-drop</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
            <prop key="hibernate.show_sql">true</prop>
         </props>
      </property>
  </bean>

  <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
  </bean>

  <context:component-scan base-package="com.littleworld.todo" />

  <jpa:repositories
    base-package="com.littleworld.todo.services"
    factory-class="org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean"
    transaction-manager-ref="transactionManager"
    entity-manager-factory-ref="entityManagerFactory" />

</beans>

Form

The Angular form

This is the angular form with its javascript file
<!doctype html>
<html ng-app='todo'>
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">

    <script src='todo.js'></script>
</head>
<body>


<div ng-controller="TodoCtrl" style="width: 60%">
  <form class="form-horizontal" ng-submit="updateTodo">
     
    <div class="control-group">
      <label class="col-sm-2 control-label" for="fn-task">task</label>
      <div class="controls">
          <input class="form-control" id="fn-task" type="text" ng-model="todo.task"
                 placeholder="task" required="required"/>
      </div>
    </div>
    <div class="form-actions">
      <button type="submit" class="btn btn-primary" ng-click="save()" ng-model-instant>
        <a class="icon-plus"></a> Save
      </button>
    </div>
  </form>
  <table class="table">
    <tr ng-repeat="todo in todos">
        <td>{{todo.task}}</td><td><button class="btn" ng-click="delete(todo.id)">delete</button></td>
    </tr>
  </table>
</div>
</body>
</html>


angular.module('todo', []).controller("TodoCtrl", function($scope, $http) {

  $scope.load = function ()  {
    $http.get('todo').
      success(function(data, status, headers, config) {
        $scope.todos = data;
      }).
      error(function(data, status, headers, config) {
        // log error
      });
  };

  $scope.save = function ()  {
    $http.post('todo', angular.toJson($scope.todo)).success(function () { 
    	$scope.load();
    });
  };

  $scope.delete = function (id)  {
    $http.delete("todo/" + id).success(function () { 
    	$scope.load();
    });
  };
});

Controllers

The controller contains the CRUD methods: create, findAll and delete

/* generated by: ControllerGenerator Tue Dec 06 14:16:47 CET 2016 */
package com.littleworld.todo.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;

import com.littleworld.todo.model.*;
import com.littleworld.todo.services.*;

@Controller
public class TodoController {
  
  @Autowired  private TodoService todoService;

  //curl -H "Content-Type: application/json" -X POST -d '{"id": 0, "task": "hallo"}' http://localhost:8080/todo/todo
  @ResponseBody
  @RequestMapping(value = "/todo", method = RequestMethod.POST)
  public int create(@RequestBody Todo todo) {
    return todoService.save(todo).getId();
  }

  //curl -H "Content-Type: application/json" -X PUT -d '{"id": 1, "task": "taskTest"}' http://localhost:8080/todo/todo/1
  @ResponseBody
  @RequestMapping(value = "/todo/{id}", method = RequestMethod.PUT)
  public int updateTodo(@PathVariable  int id, @RequestBody Todo todo) {
    return todoService.save(todo).getId();
  }

  //curl -X DELETE http://localhost:8080/todo/todo/1
  @ResponseStatus(value = HttpStatus.OK)
  @RequestMapping(value = "/todo/{id}", method = RequestMethod.DELETE)
  public void updateTodo(@PathVariable  int id) {
    todoService.delete(id);
  }

  //curl  http://localhost:8080/todo/todo
  @ResponseBody
  @RequestMapping(value = "/todo", method = RequestMethod.GET)
  public List<Todo> findAll() {
    return (List<Todo>)todoService.findAll();
  }

  //curl  http://localhost:8080/todo/todo/1
  @ResponseBody
  @RequestMapping(value = "/todo/{id}", method = RequestMethod.GET)
  public Todo todoById(@PathVariable  int id) {
    return todoService.findOne(id);
  }

  @RequestMapping(value = "/todo/page", method = RequestMethod.GET)
  public String page() {
      return "todo";
  }
}

Services

The Service is implementatie as Spring CRUD Respository The CRUD has the methods: save, findAll, findOne, delete

package com.littleworld.todo.services;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.littleworld.todo.model.*;

@Repository
public interface TodoService extends CrudRepository<Todo, Integer> {

}

Run the application

To run the application. Goto the root directory of the application and type

(Maven should in the enviroment variable PATH; and JAVA_HOME should be set to the jdk installation dir)

mvn jetty:run
http://localhost:8080/todo/page