├── .bowerrc ├── .gitignore ├── README.md ├── bower.json ├── pom.xml └── src └── main ├── java └── be │ └── g00glen00b │ ├── config │ ├── AppConfig.java │ ├── WebAppInitializer.java │ └── WebConfig.java │ ├── controller │ └── AppController.java │ ├── model │ └── Item.java │ └── repository │ └── ItemRepository.java ├── resources └── logback.xml └── webapp ├── WEB-INF └── views │ └── index.jsp └── app ├── app.js ├── controllers.js └── services.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "src/main/webapp/libs", 3 | "json": "bower.json" 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | libs/ 2 | target/ 3 | .settings/ 4 | .classpath 5 | .project -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Spring Data REST + AngularJS example 2 | ============== 3 | This example uses [Spring Data REST](http://projects.spring.io/spring-data-rest/) for easily creating RESTful webservices by writing a model and your repository (interface). On the front-end side this application uses AngularJS and [angular-spring-data-rest](http://guylabs.ch/project/angular-spring-data-rest/) for handling the Spring HATEOAS response. 4 | 5 | Make sure you follow [the tutorial](http://g00glen00b.be/spring-data-angular/) for creating apps like this. 6 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-spring-data", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "angular": "~1.3.0", 6 | "angular-spring-data-rest": "~0.3.0", 7 | "bootstrap-css-only": "~3.2.0", 8 | "lodash": "~2.4.1" 9 | } 10 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | be.g00glen00b 5 | ng-spring-data 6 | war 7 | 0.0.1-SNAPSHOT 8 | Spring web application using AngularJS and Spring data REST 9 | http://g00glen00b.be/spring-data-rest-angular 10 | 11 | 12 | ng-spring-data 13 | 14 | 15 | maven-compiler-plugin 16 | 3.1 17 | 18 | 1.7 19 | 1.7 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-war-plugin 25 | 2.4 26 | 27 | false 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.springframework 36 | spring-webmvc 37 | 4.1.1.RELEASE 38 | 39 | 40 | org.springframework.data 41 | spring-data-rest-webmvc 42 | 2.2.0.RELEASE 43 | 44 | 45 | org.springframework.data 46 | spring-data-jpa 47 | 1.7.0.RELEASE 48 | 49 | 50 | org.hibernate 51 | hibernate-entitymanager 52 | 4.3.6.Final 53 | 54 | 55 | org.hsqldb 56 | hsqldb 57 | 2.3.2 58 | 59 | 60 | javax.servlet 61 | javax.servlet-api 62 | 3.1.0 63 | provided 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/main/java/be/g00glen00b/config/AppConfig.java: -------------------------------------------------------------------------------- 1 | package be.g00glen00b.config; 2 | 3 | import static org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO; 4 | 5 | import java.net.*; 6 | import java.util.Properties; 7 | 8 | import javax.sql.DataSource; 9 | 10 | import org.springframework.context.annotation.*; 11 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 12 | import org.springframework.data.rest.core.config.RepositoryRestConfiguration; 13 | import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration; 14 | import org.springframework.jdbc.datasource.embedded.*; 15 | import org.springframework.orm.jpa.*; 16 | import org.springframework.orm.jpa.vendor.*; 17 | import org.springframework.stereotype.Controller; 18 | 19 | @Configuration 20 | @EnableJpaRepositories(basePackages = { "be.g00glen00b.repository" }) 21 | @ComponentScan(basePackages = "be.g00glen00b", excludeFilters = { 22 | @ComponentScan.Filter(value = Controller.class, type = FilterType.ANNOTATION), 23 | @ComponentScan.Filter(value = Configuration.class, type = FilterType.ANNOTATION) 24 | }) 25 | public class AppConfig extends RepositoryRestMvcConfiguration { 26 | 27 | @Override 28 | protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) { 29 | super.configureRepositoryRestConfiguration(config); 30 | try { 31 | config.setBaseUri(new URI("/api")); 32 | } catch (URISyntaxException e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | 37 | @Bean 38 | public DataSource dataSource() { 39 | return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).build(); 40 | } 41 | 42 | @Bean 43 | public JpaVendorAdapter jpaVendorAdapter() { 44 | HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter(); 45 | adapter.setShowSql(true); 46 | adapter.setGenerateDdl(true); 47 | adapter.setDatabase(Database.HSQL); 48 | return adapter; 49 | } 50 | 51 | @Bean 52 | public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws ClassNotFoundException { 53 | LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); 54 | factoryBean.setDataSource(dataSource()); 55 | factoryBean.setPackagesToScan("be.g00glen00b.model"); 56 | factoryBean.setJpaVendorAdapter(jpaVendorAdapter()); 57 | factoryBean.setJpaProperties(jpaProperties()); 58 | 59 | return factoryBean; 60 | } 61 | 62 | @Bean 63 | public JpaTransactionManager transactionManager() throws ClassNotFoundException { 64 | JpaTransactionManager transactionManager = new JpaTransactionManager(); 65 | transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); 66 | 67 | return transactionManager; 68 | } 69 | 70 | @Bean 71 | public Properties jpaProperties() { 72 | Properties properties = new Properties(); 73 | properties.put(HBM2DDL_AUTO, "create-drop"); 74 | return properties; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/be/g00glen00b/config/WebAppInitializer.java: -------------------------------------------------------------------------------- 1 | package be.g00glen00b.config; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | 5 | import javax.servlet.*; 6 | 7 | import org.springframework.web.filter.CharacterEncodingFilter; 8 | import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; 9 | 10 | public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { 11 | 12 | @Override 13 | protected void customizeRegistration(ServletRegistration.Dynamic registration) { 14 | registration.setInitParameter("dispatchOptionsRequest", "true"); 15 | registration.setAsyncSupported(true); 16 | } 17 | 18 | @Override 19 | protected Class[] getRootConfigClasses() { 20 | return new Class[] { AppConfig.class }; 21 | } 22 | 23 | @Override 24 | protected Class[] getServletConfigClasses() { 25 | return new Class[] { WebConfig.class }; 26 | } 27 | 28 | @Override 29 | protected String[] getServletMappings() { 30 | return new String[] { "/" }; 31 | } 32 | 33 | @Override 34 | protected Filter[] getServletFilters() { 35 | CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); 36 | characterEncodingFilter.setEncoding(StandardCharsets.UTF_8.name()); 37 | return new Filter[] { characterEncodingFilter }; 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/java/be/g00glen00b/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package be.g00glen00b.config; 2 | 3 | import org.springframework.context.annotation.*; 4 | import org.springframework.web.servlet.config.annotation.*; 5 | import org.springframework.web.servlet.mvc.WebContentInterceptor; 6 | import org.springframework.web.servlet.view.InternalResourceViewResolver; 7 | 8 | @Configuration 9 | @EnableWebMvc 10 | @ComponentScan(basePackages = "be.g00glen00b.controller") 11 | public class WebConfig extends WebMvcConfigurerAdapter { 12 | 13 | @Bean 14 | public InternalResourceViewResolver getInternalResourceViewResolver() { 15 | InternalResourceViewResolver resolver = new InternalResourceViewResolver(); 16 | resolver.setPrefix("/WEB-INF/views/"); 17 | resolver.setSuffix(".jsp"); 18 | return resolver; 19 | } 20 | 21 | @Override 22 | public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { 23 | configurer.enable(); 24 | } 25 | 26 | @Bean 27 | public WebContentInterceptor webContentInterceptor() { 28 | WebContentInterceptor interceptor = new WebContentInterceptor(); 29 | interceptor.setCacheSeconds(0); 30 | interceptor.setUseExpiresHeader(true); 31 | interceptor.setUseCacheControlHeader(true); 32 | interceptor.setUseCacheControlNoStore(true); 33 | 34 | return interceptor; 35 | } 36 | 37 | @Override 38 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 39 | registry.addResourceHandler("/libs/**").addResourceLocations("/libs/"); 40 | registry.addResourceHandler("/app/**").addResourceLocations("/app/"); 41 | registry.addResourceHandler("/assets/**").addResourceLocations("/assets/"); 42 | } 43 | 44 | @Override 45 | public void addInterceptors(InterceptorRegistry registry) { 46 | registry.addInterceptor(webContentInterceptor()); 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/java/be/g00glen00b/controller/AppController.java: -------------------------------------------------------------------------------- 1 | package be.g00glen00b.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.*; 5 | 6 | @Controller 7 | @RequestMapping("/") 8 | public class AppController { 9 | 10 | @RequestMapping(method = RequestMethod.GET) 11 | public String viewApplication() { 12 | return "index"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/be/g00glen00b/model/Item.java: -------------------------------------------------------------------------------- 1 | package be.g00glen00b.model; 2 | 3 | import javax.persistence.*; 4 | 5 | @Entity 6 | public class Item { 7 | 8 | @Id 9 | @GeneratedValue(strategy=GenerationType.IDENTITY) 10 | private int id; 11 | 12 | @Column 13 | private boolean checked; 14 | 15 | @Column 16 | private String description; 17 | 18 | public int getId() { 19 | return id; 20 | } 21 | 22 | public void setId(int id) { 23 | this.id = id; 24 | } 25 | 26 | public boolean isChecked() { 27 | return checked; 28 | } 29 | 30 | public void setChecked(boolean checked) { 31 | this.checked = checked; 32 | } 33 | 34 | public String getDescription() { 35 | return description; 36 | } 37 | 38 | public void setDescription(String description) { 39 | this.description = description; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/be/g00glen00b/repository/ItemRepository.java: -------------------------------------------------------------------------------- 1 | package be.g00glen00b.repository; 2 | 3 | import org.springframework.data.repository.PagingAndSortingRepository; 4 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 5 | 6 | import be.g00glen00b.model.Item; 7 | 8 | @RepositoryRestResource(collectionResourceRel = "items", path = "items") 9 | public interface ItemRepository extends PagingAndSortingRepository { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/views/index.jsp: -------------------------------------------------------------------------------- 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 | 47 | -------------------------------------------------------------------------------- /src/main/webapp/app/app.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | angular.module("myApp.controllers", []); 3 | angular.module("myApp.services", []); 4 | angular.module("myApp", ["ngResource", "spring-data-rest", "myApp.controllers", "myApp.services"]); 5 | }(angular)); -------------------------------------------------------------------------------- /src/main/webapp/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.save(); 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/webapp/app/services.js: -------------------------------------------------------------------------------- 1 | (function(angular) { 2 | var HATEOAS_URL = './api/items'; 3 | var ItemFactory = function($http, SpringDataRestAdapter) { 4 | function Item(item) { 5 | 6 | if (item._resources) { 7 | item.resources = item._resources("self", {}, { 8 | update: { 9 | method: 'PUT' 10 | } 11 | }); 12 | item.save = function(callback) { 13 | item.resources.update(item, function() { 14 | callback && callback(item); 15 | }); 16 | }; 17 | 18 | item.remove = function(callback) { 19 | item.resources.remove(function() { 20 | callback && callback(item); 21 | }); 22 | }; 23 | } else { 24 | item.save = function(callback) { 25 | Item.resources.save(item, function(item, headers) { 26 | var deferred = $http.get(headers().location); 27 | return SpringDataRestAdapter.processWithPromise(deferred).then(function(newItem) { 28 | callback && callback(new Item(newItem)); 29 | }); 30 | }); 31 | }; 32 | } 33 | 34 | return item; 35 | } 36 | 37 | Item.query = function(callback) { 38 | var deferred = $http.get(HATEOAS_URL); 39 | return SpringDataRestAdapter.processWithPromise(deferred).then(function(data) { 40 | Item.resources = data._resources("self"); 41 | callback && callback(_.map(data._embeddedItems, function(item) { 42 | return new Item(item); 43 | })); 44 | }); 45 | }; 46 | 47 | Item.resources = null; 48 | 49 | return Item; 50 | }; 51 | 52 | ItemFactory.$inject = ['$http', 'SpringDataRestAdapter']; 53 | angular.module("myApp.services").factory("Item", ItemFactory); 54 | }(angular)); --------------------------------------------------------------------------------