├── .bowerrc ├── .gitignore ├── LICENSE ├── README.md ├── bower.json ├── pom.xml └── src └── main ├── java └── be │ └── g00glen00b │ ├── Application.java │ ├── controller │ └── ItemController.java │ ├── model │ └── Item.java │ └── repository │ └── ItemRepository.java └── resources ├── application.properties └── static ├── app ├── app.js ├── controllers.js └── services.js └── index.html /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "src/main/resources/static/bower_components", 3 | "json": "bower.json" 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | .settings/ 4 | target/ 5 | bower_components/ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) {{{year}}} {{{fullname}}} 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rapid prototyping with Spring Boot and AngularJS 2 | This example demonstrates how **Spring Boot**, **Spring Data JPA** and in the front-end **AngularJS** can be used together to write web applications easily. 3 | In this code example I'm demonstrating this by providing a full CRUD-based web application in about 150 lines of code (100 lines of Java code and 50 lines of JavaScript code). 4 | 5 | ## Frameworks 6 | 7 | ### Front-end 8 | 9 | #### Twitter Bootstrap 10 | For rapidly creating prototypes of a web application, a UI toolkit or library will become really handy. There are many choices available, and for this example I chose Twitter Bootstrap. 11 | 12 | #### AngularJS 13 | AngularJS is a MVC based framework for web applications, written in JavaScript. It makes it possible to use the Model-View-Controller pattern on the front-end. It also comes with several additional modules. In this example I'm also using **angular-resource**, which is a simple factory-pattern based module for creating REST clients. 14 | 15 | ### Back-end 16 | 17 | #### Spring Boot 18 | One of the hassles while creating web applications using the Spring Framework is that it involves a lot of configuration. Spring Boot makes it possible to write configuration-less web application because it does a lot for you out of the box. 19 | For example, if you add HSQLDB as a dependency to your application, it will automatically provide a datasource to it. 20 | If you add the spring-boot-starter-web dependency, then you can start writing controllers for creating a web application. 21 | 22 | #### Spring Data JPA 23 | Spring Data JPA allows you to create repositories for your data without even having to write a lot of code. The only code you need is a simple interface that extends from another interface and then you're done. 24 | With Spring Boot you can even leave the configuration behind for configuring Spring Data JPA, so now it's even easier. 25 | 26 | ## Installation 27 | Installation is quite easy, first you will have to install some front-end dependencies using Bower: 28 | ``` 29 | bower install 30 | ``` 31 | 32 | Then you can run Maven to package the application: 33 | ``` 34 | mvn clean package 35 | ``` 36 | 37 | Now you can run the Java application quite easily: 38 | ``` 39 | cd target 40 | java -jar ng-spring-boot-1.0.0.jar 41 | ``` 42 | 43 | ## Related articles 44 | 45 | - [Rapid prototyping with Spring Boot and AngularJS](http://g00glen00b.be/prototyping-spring-boot-angularjs/) 46 | - [Easy integration testing with Spring Boot and REST-Assured](http://g00glen00b.be/spring-boot-rest-assured/) 47 | - [Unit testing with Mockito and AssertJ](http://g00glen00b.be/unit-testing-mockito-assertj/) 48 | - [Executing Jasmine tests with Maven](http://g00glen00b.be/jasmine-tests-maven/) 49 | - [Testing your Spring Data JPA repository](http://g00glen00b.be/testing-spring-data-repository/) 50 | - [Testing your Spring Boot application with Selenium](http://g00glen00b.be/spring-boot-selenium/) 51 | 52 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-spring-boot", 3 | "dependencies": { 4 | "angular": "~1.3.0", 5 | "angular-resource": "~1.3.0", 6 | "bootstrap-css-only": "~3.2.0" 7 | } 8 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | be.g00glen00b 5 | ng-spring-boot 6 | 1.0.0 7 | ng-spring-boot 8 | 9 | 10 | org.springframework.boot 11 | spring-boot-starter-parent 12 | 1.2.0.RELEASE 13 | 14 | 15 | 16 | 17 | 18 | org.hsqldb 19 | hsqldb 20 | 2.3.2 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-maven-plugin 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-web 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-data-jpa 42 | 43 | 44 | org.hsqldb 45 | hsqldb 46 | runtime 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/main/java/be/g00glen00b/Application.java: -------------------------------------------------------------------------------- 1 | package be.g00glen00b; 2 | 3 | 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.*; 6 | 7 | @SpringBootApplication 8 | public class Application { 9 | 10 | public static void main(String[] args) { 11 | SpringApplication.run(Application.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/be/g00glen00b/controller/ItemController.java: -------------------------------------------------------------------------------- 1 | package be.g00glen00b.controller; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.*; 7 | 8 | import be.g00glen00b.model.Item; 9 | import be.g00glen00b.repository.ItemRepository; 10 | 11 | @RestController 12 | @RequestMapping("/items") 13 | public class ItemController { 14 | @Autowired 15 | private ItemRepository repo; 16 | 17 | @RequestMapping(method = RequestMethod.GET) 18 | public List findItems() { 19 | return repo.findAll(); 20 | } 21 | 22 | @RequestMapping(method = RequestMethod.POST) 23 | public Item addItem(@RequestBody Item item) { 24 | item.setId(null); 25 | return repo.saveAndFlush(item); 26 | } 27 | 28 | @RequestMapping(value = "/{id}", method = RequestMethod.PUT) 29 | public Item updateItem(@RequestBody Item updatedItem, @PathVariable Integer id) { 30 | updatedItem.setId(id); 31 | return repo.saveAndFlush(updatedItem); 32 | } 33 | 34 | @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) 35 | public void deleteItem(@PathVariable Integer id) { 36 | repo.delete(id); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/be/g00glen00b/model/Item.java: -------------------------------------------------------------------------------- 1 | package be.g00glen00b.model; 2 | 3 | import javax.persistence.*; 4 | 5 | 6 | @Entity 7 | public class Item { 8 | @Id 9 | @GeneratedValue(strategy=GenerationType.IDENTITY) 10 | private Integer id; 11 | @Column 12 | private boolean checked; 13 | @Column 14 | private String description; 15 | 16 | public Integer getId() { 17 | return id; 18 | } 19 | 20 | public void setId(Integer id) { 21 | this.id = id; 22 | } 23 | 24 | public boolean isChecked() { 25 | return checked; 26 | } 27 | 28 | public void setChecked(boolean checked) { 29 | this.checked = checked; 30 | } 31 | 32 | public String getDescription() { 33 | return description; 34 | } 35 | 36 | public void setDescription(String description) { 37 | this.description = description; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/be/g00glen00b/repository/ItemRepository.java: -------------------------------------------------------------------------------- 1 | package be.g00glen00b.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | import be.g00glen00b.model.Item; 6 | 7 | public interface ItemRepository extends JpaRepository { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.jpa.hibernate.ddl-auto=create-drop -------------------------------------------------------------------------------- /src/main/resources/static/app/app.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | angular.module("myApp.controllers", []); 3 | angular.module("myApp.services", []); 4 | angular.module("myApp", ["ngResource", "myApp.controllers", "myApp.services"]); 5 | }(angular)); -------------------------------------------------------------------------------- /src/main/resources/static/app/controllers.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | var AppController = function($scope, Item) { 3 | Item.query(function(response) { 4 | $scope.items = response ? response : []; 5 | }); 6 | 7 | $scope.addItem = function(description) { 8 | new Item({ 9 | description: description, 10 | checked: false 11 | }).$save(function(item) { 12 | $scope.items.push(item); 13 | }); 14 | $scope.newItem = ""; 15 | }; 16 | 17 | $scope.updateItem = function(item) { 18 | item.$update(); 19 | }; 20 | 21 | $scope.deleteItem = function(item) { 22 | item.$remove(function() { 23 | $scope.items.splice($scope.items.indexOf(item), 1); 24 | }); 25 | }; 26 | }; 27 | 28 | AppController.$inject = ['$scope', 'Item']; 29 | angular.module("myApp.controllers").controller("AppController", AppController); 30 | }(angular)); -------------------------------------------------------------------------------- /src/main/resources/static/app/services.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | var ItemFactory = function($resource) { 3 | return $resource('/items/:id', { 4 | id: '@id' 5 | }, { 6 | update: { 7 | method: "PUT" 8 | }, 9 | remove: { 10 | method: "DELETE" 11 | } 12 | }); 13 | }; 14 | 15 | ItemFactory.$inject = ['$resource']; 16 | angular.module("myApp.services").factory("Item", ItemFactory); 17 | }(angular)); -------------------------------------------------------------------------------- /src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 11 | 14 |
15 |
16 |
17 | 20 |
21 |
22 | 26 |
27 |
28 |
29 |
30 | 31 | 32 | 35 | 36 |
37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | --------------------------------------------------------------------------------