Building modern webapps using Spring Data REST and AngularJS

Recently I wrote several “exotic” applications using WebSockets. If you’re not into WebSockets, but you’re interested in using Spring and AngularJS, this article may suite you. In this example I will setup a web project using the Spring framework and an in memory embedded HSQL database. The client-side of the application will be written using AngularJS. Let’s start!

Project setup

I’m gonna start of with a simple Maven webproject, in my pom.xml I will have to add the following dependencies:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>4.1.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-rest-webmvc</artifactId>
    <version>2.2.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>1.7.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>4.3.6.Final</version>
</dependency>
<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <version>2.3.2</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>

I will also need several front-end dependencies, I will use Bower to manage them:

{
    "name": "ng-spring-data",
    "dependencies": {
        "angular": "~1.3.0",
        "angular-spring-data-rest": "~0.3.0",
        "bootstrap-css-only": "~3.2.0",
        "lodash": "~2.4.1"
    }
}

To change the location of where Bower installs the libraries, I’m also going to add a .bowerrc folder to the root of my project, containing the following:

{
    "directory": "src/main/webapp/libs",
    "json": "bower.json"
}

If you’re asking yourself why we need all these libraries, well, here’s a description of why:

  • spring-webmvc: The Web MVC framework will be used for having the MVC pattern for loading our web application.
  • spring-data-rest: The Spring Data REST framework allows us to create RESTful webservices of our model quite easily using Spring HATEOAS.
  • spring-data-jpa: Spring Data JPA is used to create JPA enabled repositories from our models, allowing you to use the repository pattern to work with your data.
  • hibernate-entitymanager: JPA provides a cool API, but to actually use it, you will need an implementation like Hibernate or OpenJPA. In this case I’m going to use Hibernate, so I need this library as well.
  • hsqldb: For storing our data we will be using an in memory HSQLDB. We also need to be able to connect to it, so we also use this library for having our JDBC driver.
  • javax.servlet-api: To use JavaConfig we will need the servlet-api 3, and to use it in our code, we will have to add it as well. However, the web container will usually have this one as well, so we’ve set the scope to provided for this one.

Then we’re also using several front-end libraries:

  • angular: AngularJS allows us for using the MVC pattern for the client-side part of the application.
  • angular-spring-data-rest: AngularJS has great integration with REST API’s, however, the integration with HATEOAS based RESTful webservices is less, so we will need to use an external library for that.
  • bootstrap-css-only: We don’t want to waste a lot of time setting up the user interface of the application, so using a UI library like Bootstrap, Foundation or Semantic UI really helps.
  • lodash: JavaScript is great, but there are several tools which you will use a lot that aren’t available out of the box, so it’s a good idea to have a utility belt library with you like Lo-Dash or Underscore.js.

WebAppInitializer

Modern webapps no longer contain files like web descriptors or Spring bean configuration files. With JavaConfig you can properly replace your web descriptor with a class. The contents will look similar to those of a web descriptor though:

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

  @Override
  protected void customizeRegistration(ServletRegistration.Dynamic registration) {
    registration.setInitParameter("dispatchOptionsRequest", "true");
    registration.setAsyncSupported(true);
  }

  @Override
  protected Class<?>[] getRootConfigClasses() {
    return new Class<?>[] { AppConfig.class };
  }

  @Override
  protected Class<?>[] getServletConfigClasses() {
    return new Class<?>[] { WebConfig.class };
  }

  @Override
  protected String[] getServletMappings() {
    return new String[] { "/" };
  }

  @Override
  protected Filter[] getServletFilters() {
    CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
    characterEncodingFilter.setEncoding(StandardCharsets.UTF_8.name());
    return new Filter[] { characterEncodingFilter };
  }
}

We still have servlets, mappings and filters. In this case I’m gonna map a servlet to the root path and have the class WebConfig handle the web context (similar to what springmvc-servlet.xml did in the past).
The application context is also replaced and now we have a class called AppConfig which replaces the applicationContext.xml file.

Finally I also added a filter that makes sure that everything is encoded as UTF-8. If you don’t do that, you may get some strange responses when having special characters.

Setting up the application context

We have to implement two other configuration files called AppConfig and WebConfig. The first one, AppConfig, contains mostly the configuration for setting up the datasource and the entity manager:

@Configuration
@EnableJpaRepositories(basePackages = { "be.g00glen00b.repository" })
@ComponentScan(basePackages = "be.g00glen00b", excludeFilters = {
    @ComponentScan.Filter(value = Controller.class, type = FilterType.ANNOTATION),
    @ComponentScan.Filter(value = Configuration.class, type = FilterType.ANNOTATION)
})
public class AppConfig extends RepositoryRestMvcConfiguration {
  
  @Override
  protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
    super.configureRepositoryRestConfiguration(config);
    try {
      config.setBaseUri(new URI("/api"));
    } catch (URISyntaxException e) {
      e.printStackTrace();
    }
  }

  @Bean
  public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).build();
  }

  @Bean
  public JpaVendorAdapter jpaVendorAdapter() {
    HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
    adapter.setShowSql(true);
    adapter.setGenerateDdl(true);
    adapter.setDatabase(Database.HSQL);
    return adapter;
  }

  @Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws ClassNotFoundException {
    LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
    factoryBean.setDataSource(dataSource());
    factoryBean.setPackagesToScan("be.g00glen00b.model");
    factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
    factoryBean.setJpaProperties(jpaProperties());

    return factoryBean;
  }

  @Bean
  public JpaTransactionManager transactionManager() throws ClassNotFoundException {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());

    return transactionManager;
  }

  @Bean
  public Properties jpaProperties() {
    Properties properties = new Properties();
    properties.put(HBM2DDL_AUTO, "create-drop");
    return properties;
  }
}

Let’s see what this class contains. Starting from the top we notice several annotations to enable features in our application. First of all we have @EnableJpaRepositories to indicate we’re going to use Spring Data JPA and that we have our repositories inside the package be.g00glen00b.repository.

Also, we have to scan all packages for beans, which we do by using the @ComponentScan annotation. However, we don’t have to scan controllers, since they’re part of the web context and neither doe we have to scan configuration files, because they have to be loaded in a different way. That’s why we add them to the exclusion filter (excludeFilters).

Then, when looking at the class, you can see we’re inheriting from RepositoryRestMvcConfiguration. This allows us to create RESTful resources from our repositories.

This also means we have to override the configureRepositoryRestConfiguration() method, in which we can define the root path of these RESTful resources.

The other methods like dataSource(), jpaVendorAdapter(), entityManagerFactory(), transactionManager() and jpaProperties() are used for setting up our datasource and JPA.

Setting up the web context

We’re now able to create RESTful webservices, however, our application also has a user interface, so we will have to configure our web context as well using WebConfig:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "be.g00glen00b.controller")
public class WebConfig extends WebMvcConfigurerAdapter {

  @Bean
  public InternalResourceViewResolver getInternalResourceViewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".jsp");
    return resolver;
  }

  @Override
  public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
  }

  @Bean
  public WebContentInterceptor webContentInterceptor() {
    WebContentInterceptor interceptor = new WebContentInterceptor();
    interceptor.setCacheSeconds(0);
    interceptor.setUseExpiresHeader(true);
    interceptor.setUseCacheControlHeader(true);
    interceptor.setUseCacheControlNoStore(true);

    return interceptor;
  }

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/libs/**").addResourceLocations("/libs/");
    registry.addResourceHandler("/app/**").addResourceLocations("/app/");
    registry.addResourceHandler("/assets/**").addResourceLocations("/assets/");
  }

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(webContentInterceptor());
  }
}

Starting from the top again, you can see that we use the #EnableWebMvc annotation to indicate that we’re using Spring Web MVC.

Our class extends from WebMvcConfigurerAdapter which allows us to add interceptors (addInterceptors()) and resource handlers (addResourceHandlers()).

We’re adding the webContentInterceptor() interceptor that allows use to set cache controlling.
The resource handlers are used to indicate which paths should be used to serve static resources. In this case it will be the libsapp and assets folder.

Then finally, we also have to create a bean called the getInternalResourceViewResolver() bean. This bean is used when defining controllers so that it’s able to resolve the location of the view used inside the controller.

Adding a controller

Speaking of a controller… let’s implement it now we’re busy. Make sure that you’re adding the controller to the be.g00glen00b.controller package, because that’s the location used inside the component scan of the WebConfig.

The controller itself is quite easy, it has only one method, used to load the main application page:

@Controller
@RequestMapping("/")
public class AppController {

  @RequestMapping(method = RequestMethod.GET)
  public String viewApplication() {
    return "index";
  }
}

As you can see, it’s a quite simple method, that only returns the string "index". Returning in a string means that we’re using a view called index which matches the file /WEB-INF/views/index.jsp thanks to the getInternalResourceViewResolver() bean in our configuration.

Defining the model

The application I’m going to build will be a simple todo application, but if you’re interested in building something else, you simply define another model.

A todo item can have a description and it can be checked or unchecked. We also need to add an ID, which will be used to know which todo item we’re talking about.

THe model will eventually look like this:

@Entity
public class Item {

  @Id
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  private int id;
  
  @Column
  private boolean checked;
  
  @Column
  private String description;

  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  public boolean isChecked() {
    return checked;
  }

  public void setChecked(boolean checked) {
    this.checked = checked;
  }

  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }
}

Make sure you add this class to the be.g00glen00b.model package, because this is the only package we said to scan inside the entityManagerFactory.

Creating the heart of our application, the repository

All we have to do now is to create a repository based upon the model. Creating a repository with the Spring Data JPA framework is quite easy though, we only have to define a simple interface like this:

@RepositoryRestResource(collectionResourceRel = "items", path = "items")
public interface ItemRepository extends PagingAndSortingRepository<Item, Integer> {

}

This adds our JPA repository and enables it for REST as well. If you deploy your application now and open your web browser, you can already access the REST API by going to:

http://localhost:8080/ng-spring-data/api/items

In this case /ng-spring-data is my content root and I’m running my web container on http://localhost:8080.

rest-response

Working at the front-end

Before we start with writing JavaScript code, we have to create the HTML page first. As I mentioned before, we mapped the controller so that when the application is opened, the /WEB-INF/views/index.jsp file is used as the view. So, let’s write that file. The content is quite simple:

<!DOCTYPE html>
<html lang="en">
  <head>
    <link rel="stylesheet" href="./libs/bootstrap-css-only/css/bootstrap.min.css" />
  </head>
  <body ng-app="myApp">
    <div class="container" ng-controller="AppController">
      <div class="page-header">
        <h1>A checklist</h1>
      </div>
      <div class="alert alert-info" role="alert" ng-hide="items && items.length > 0">
        There are no items yet.
      </div>
      <form class="form-horizontal" role="form" ng-submit="addItem(newItem)">
        <div class="form-group" ng-repeat="item in items">
          <div class="checkbox col-xs-9">
            <label>
              <input type="checkbox" ng-model="item.checked" ng-change="updateItem(item)"/> {{item.description}}
            </label>
          </div>
          <div class="col-xs-3">
            <button class="pull-right btn btn-danger" type="button" title="Delete"
              ng-click="deleteItem(item)">
              <span class="glyphicon glyphicon-trash"></span>
            </button>
          </div>
        </div>
        <hr />
        <div class="input-group">
          <input type="text" class="form-control" ng-model="newItem" placeholder="Enter the description..." />
          <span class="input-group-btn">
            <button class="btn btn-default" type="submit" ng-disabled="!newItem" title="Add">
              <span class="glyphicon glyphicon-plus"></span>
            </button>
          </span>
        </div>
      </form>
    </div>
    <script type="text/javascript" src="./libs/angular/angular.min.js"></script>
    <script type="text/javascript" src="./libs/angular-resource/angular-resource.min.js"></script>
    <script type="text/javascript" src="./libs/angular-spring-data-rest/dist/angular-spring-data-rest.min.js"></script>
    <script type="text/javascript" src="./libs/lodash/dist/lodash.min.js"></script>
    <script type="text/javascript" src="./app/app.js"></script>
    <script type="text/javascript" src="./app/controllers.js"></script>
    <script type="text/javascript" src="./app/services.js"></script>
  </body>
</html>

As you can see, most of it is simple HTML with some Twitter Bootstrap classes. However, you will also notice some other attributes like ng-app and <code>ng-controller. These are AngularJS directives which contain the logic for translating this HTML template into the actual HTML visible in the application.

Bootstrapping AngularJS

First of all, we have the ng-app attribute, indicating that we have to bootstrap AngularJS in our application and allows us to pass it a name so that we can configure which application modules that should be used.
A few lines below we find the ng-controller="AppController" attribute. This means that that part of the application is controller by a controller named AppController.

The ng-controller attribute does refer to an AngularJS controller, not a Spring MVC controller!

Hiding components

The next AngularJS directive has a bit more logic and shows the real power of these directives. The ng-hide directive allows you to hide specific elements if the expression passed to it validates to true. In this case we’re using it to show a message in case there is no todo item yet, so I wrote the following: ng-hide="items && items.length > 0". This means that the items model should exist and should not be empty.

We can use the model in all our directives. For example, to loop over all items inside the items model we use the ng-repeat directive, for example ng-repeat="item in items", which means that all the elements will be repeated for each item.

Model binding

The most beautiful part of AngularJS is the possibility to have two way binding. For example, if you have a textfield, you often need the value of the textbox inside a variable in JavaScript. In AngularJS you can bind a textbox to a model (= JavaScript property in the controller), so that each time you enter a character in the textbox, the model is updated.
The two in two way binding makes the opposite possible as well. If you change the model directly, the value in the textbox will also update.
To bind the model to a form element we use the ng-model directive, for example ng-model="item.checked".

Event handling

We can do most things now, except that we have to handle events. If we’re adding new items to the todo list, we want to capture the submit event on our form. We can do that by using the ng-submit directive.

If you want to track changes, you can also use the onChange event by using the ng-change directive, like you can see on the checkbox.
You can also use the ng-click directive to handle clicks, like we did on the delete button.

The functions we pass to these directives are functions defined in our controller.

Defining the controller

As you may have noticed now, the controller is the heart of our applications that controls all the small blocks in our application (= the directives).

As you may have noticed from the JSP page, we’re including three scripts we didn’t define until now: app.js, controllers.js and services.js.

In controllers.js we will define our controller, the AppController:

(function(angular) {
  var AppController = function($scope, Item) {
    Item.query(function(response) {
      $scope.items = response ? response : [];
    });
    
    $scope.addItem = function(description) {
      new Item({
        description: description,
        checked: false
      }).save(function(item) {
        $scope.items.push(item);
      });
      $scope.newItem = "";
    };
    
    $scope.updateItem = function(item) {
      item.save();
    };
    
    $scope.deleteItem = function(item) {
      item.remove(function() {
        $scope.items.splice($scope.items.indexOf(item), 1);
      });
    };
  };
  
  AppController.$inject = ['$scope', 'Item'];
  angular.module("myApp.controllers").controller("AppController", AppController);
}(angular));

As you can see here, we can find all the methods we used in our JSP page. For example, we have the functions updateItem(), deleteItem() and addItem() which we used in the event handling.

We also find the items model, called $scope.items and the newItem model called $scope.newItem.

Important to notice here is that we have no HTML code here. If we’re deleting a todo item, all we have to do is to delete it from the list of items, (which is why we use the splice() function in deleteItem()).

We can also update form elements by updating the model, if you look at addItem(), you can see that we use $scope.newItem = "", which will erase the contents of the textbox using ng-model="newItem".

As you can see, most of the things we do, have to do with the Item object. This object is injected into the controller. AngularJS allows you to inject modules into other modules, which we’re using here to inject the Item factory.

Using HATEOAS in the Item factory

What you may have noticed when viewing the REST API is that you get a lot of additional data, for example:

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/ng-spring-data/api/items{?page,size,sort}",
      "templated" : true
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}

This is called HATEOAS (Hypermedia as the Engine of Application State). Like I said before, AngularJS does not have support for this out of the box (nor in their angular-resource project).

So, to allow us to use it without polluting the controller, we abstracted it away into a separate factory, so let’s open services.js.

The code of this factory is a bit more complex, so let’s split it into smaller parts first.

First of all we have to create the module itself, for example:

(function(angular) {
  var HATEOAS_URL = './api/items';
  var ItemFactory = function($http, SpringDataRestAdapter) {
    function Item(item) {
      return item;
    }
    
    return Item;
  };
  
  ItemFactory.$inject = ['$http', 'SpringDataRestAdapter'];
  angular.module("myApp.services").factory("Item", ItemFactory);
}(angular));

This looks quite similar to the setup of the controller, except the fact that we’re using a function prototype here called Item.

Querying the RESTful webservice

Before we start implementing the prototype, I’m going to add a “static” function called query() which will access the API and return a list of items:

Item.query = function(callback) {
  var deferred = $http.get(HATEOAS_URL);
  return SpringDataRestAdapter.processWithPromise(deferred).then(function(data) {
    Item.resources = data._resources("self");
    callback && callback(_.map(data._embeddedItems, function(item) {
      return new Item(item);
    }));
  });
};

Item.resources = null;

We’re using the $http module to execute an AJAX request to the REST API and then convert the response using the angular-spring-data-rest module. Then we’re using the Item prototype to create new instances of it.

We’re also saving the resources into Item.resources.

Prototyping Item

The next step is to create the prototype of Item. If we look back at the controller, we can see that it should have two functions called save() and remove().

The save() function has two purposes in this case. When we’re creating a new item in addItem(), we’re using it to add the new item.
However, in updateItem() we’re updating an already existing item.

To make a difference between these two, we verify if the item has resources, by using item._resources. If the factory created the Item, it will have these resources.
However, if the controller made the Item, it will not have them, allowing us to provide other implementation details for the save() functionality.

So if we look at the Item prototype it will have this structure:

function Item(item) {     
  if (item._resources) {
    item.save = function(callback) { };
        
    item.remove = function(callback) { };
  } else {
    item.save = function(callback) { };
  }

  return item;
}

Saving new items

First of all, let’s write the save() function in case there are no resources. If the item itself has no resources, it has to add the todo item. However, Spring HATEOAS does not provide a response when adding the item, but it does provide the Location header, containing a link to the HATEOAS response of the new item.

So, we came up with writing something like this:

item.save = function(callback) {
  Item.resources.save(item, function(item, headers) {
    var deferred = $http.get(headers().location);
    return SpringDataRestAdapter.processWithPromise(deferred).then(function(newItem) {
      callback && callback(new Item(newItem));
    });
  });
};

It uses the Item.resources, because these resources tell us what the URL is for saving new items. After saving, we use the headers to retrieve the location header, by using headers().location and we use $http to call it using AJAX.

The response has to be promised using angular-spring-data-rest, similar to how we did inside the query() function, only this time it’s for handlign only one item.

Updating and deleting existing items

Each item individually has separate _resources, which will tell us what URL to use to update and delete the songs. So, updating and deleting is quite simple:

item.resources = item._resources("self", {}, {
  update: {
    method: 'PUT'
  }
});
item.save = function(callback) {
  item.resources.update(item, function() {
    callback && callback(item);
  });
};
        
item.remove = function(callback) {
  item.resources.remove(function() {
    callback && callback(item);
  });
};

Putting everything together in the factory we get:

(function(angular) {
  var HATEOAS_URL = './api/items';
  var ItemFactory = function($http, SpringDataRestAdapter) {
    function Item(item) {
      
      if (item._resources) {
        item.resources = item._resources("self", {}, {
          update: {
            method: 'PUT'
          }
        });
        item.save = function(callback) {
          item.resources.update(item, function() {
            callback && callback(item);
          });
        };
        
        item.remove = function(callback) {
          item.resources.remove(function() {
            callback && callback(item);
          });
        };
      } else {
        item.save = function(callback) {
          Item.resources.save(item, function(item, headers) {
            var deferred = $http.get(headers().location);
            return SpringDataRestAdapter.processWithPromise(deferred).then(function(newItem) {
              callback && callback(new Item(newItem));
            });
          });
        };
      }

      return item;
    }
    
    Item.query = function(callback) {
      var deferred = $http.get(HATEOAS_URL);
      return SpringDataRestAdapter.processWithPromise(deferred).then(function(data) {
        Item.resources = data._resources("self");
        callback && callback(_.map(data._embeddedItems, function(item) {
          return new Item(item);
        }));
      });
    };
    
    Item.resources = null;
    
    return Item;
  };
  
  ItemFactory.$inject = ['$http', 'SpringDataRestAdapter'];
  angular.module("myApp.services").factory("Item", ItemFactory);
}(angular));

Defining the application packages

The last file that’s left is the app.js file, defining the packages myApp, myApp.controllers and myApp.services:

(function(angular) {
  angular.module("myApp.controllers", []);
  angular.module("myApp.services", []);
  angular.module("myApp", ["ngResource", "spring-data-rest", "myApp.controllers", "myApp.services"]);
}(angular));

The name of the myApp module is the same name we have to enter when bootstrapping the application (ng-app="myApp").
The array we’re using as the second parameters is the list of module it depends on. In this case the controllers and services are not depending on any other modules, however, the application itself needs the ngResource, spring-data-rest, the controllers and the services modules to work.

Demo

That’s it for the development of the application. As you can see the amount of Java code we had to write was pretty small, even configuring the application took us longer than actually writing the code. The JavaScript side was a bit more complex, but I hope to see more support for HATEOAS in the future in AngularJS. I tried to make it run similar to how angular-resource works, but that came with a cost for implementing such a factory.

But let’s try out the application. If you deployed it in your web container, you should be able to see the following application in your web browser.

checklist-no-items

If you add a new item, you will see that two network requests are sent, one for adding the item and another one for retrieving the contents of that item.

checklist-item-added

Checking or unchecking a task will send a PUT request for updating the item:

checklist-item-updated

And finally, deleting will send a DELETE request:

checklist-item-deleted

That’s it for this tutorial. You’re now able to write web applications using Spring MVC, Spring Data REST and AngularJS.

Achievement: Built modern webapps using Spring Data REST and AngularJS

Seeing this means you managed to read through the entire tutorial about developing modern webapps using Spring Data REST and AngularJS. If you’re interested in the full code example, you can find it on GitHub. If you want to try out the code yourself, you can download an archive from Github.

Tagged , , , .

g00glen00b

Consultant at Cronos and Tech lead at Aquafin. Usually you can find me trying out new libraries and technologies. Loves both Java and JavaScript.

  • gwuhaolin

    here has a nice lib https://github.com/gwuhaolin/spring-data-rest-js can help you.
    It’s a easy to use and lightweight javascript lib can run in both node.js and browser.

  • kenji

    Hi, great but how we can use spring security(roles) with angularjs

  • johaness vix
  • johaness vix

    WHY DONT YOU POST IT COMPLETE!!! IT CANT WORKS WITHOUT A PROPER WEB XML

    Caused by: java.lang.IllegalArgumentException: Failed to register servlet with name ‘dispatcher’.Check if there is another servlet registered under the same name.

    CRAP!

  • johaness vix

    we can’t run! you are missing the libs files!!!!

    Why don’t you just post it complete for everything works like expected, why you insist post INCOMPLETE projects like that for making us waste time!??

  • johaness vix

    how do you deploy this thing? my tomcat 7 cannot run it and it doesnt have any web.xml

    • With Servlet 3.0 you no longer need a web descriptor. This file is completely left away and replaced by the Spring WebAppInitializer. If your container does not support Servlet 3.0, you’ll have to update or it may work by adding a dummy web descriptor.

  • johaness vix

    Supose that you have an app with thousands of pages, hundred functionalities, its angular enough? Can you organize all of those views keeping maintainability without doing a big mess?
    Don’t get me wrong but, you control the flow of pages inside Java or in Angular routes?

    • I can hardly believe having an application of that size, but yes it’s certainly possible. However, I would suggest grouping your code by feature in stead of layer (so in stead of creating a folder for controllers/services/templates/filters/directives, you should create a folder for each feature of your application, for example list/register/login/detail/detail2/list2/…).

      I would also recommend a build tool like Grunt or Gulp to concatenate your thousands of files to a single file (or a few files for each part of your application), to make sure performance is not impacted when you load your application.

      About the routing strategy… I don’t think it matters, though if you’re able to split you application in functional areas and using separate JavaScript files for each area, you might be better of with serverside routing (Java) because then you don’t have to load all files at once.

  • Aegidius

    Is it possible to retrieve an embedded items ID?

    • They’re not included in the response, but you could retrieve the ID from the link it includes with each embedded item. I’m not sure if you can override this behavior to include the ID.

  • Kaspar

    There is no main class?

    • This example is a web application packaged as a WAR file. You don’t need main methods there, because you use a web descriptor. Usually it would be web.xml, but with more recent versions of Spring we can use a Java class, in this case

      WebAppInitializer

      .

  • Aycan Adal

    Thanks for great article. I am new to angularjs and spring data rest. Few questions about the Item.query:

    Why do you use $http but not $resource?

    Why do you use SpringDataRestAdapter but not the interceptor?

    How would you query a single item?

    • I’m using

      $http

      here because

      $resource

      (imho) is more designed for non HATEOAS-resources. The URLs in HATEOAS resources should come from the response, and not a hardcoded URL in a resource, and that’s why I’m using a specific library to do that.

      The interceptor is certainly a possibility, but I tried to make a separate factory that resembles

      $resource

      a lot, but uses HATEOAS as its main resource for knowing which URLs to acces. It could be that the SpringDataRestAdapter already has some feature that already does this, but I found it very hard to get through their docs.

      Querying one item is certainly possible, but I didn’t implement it in this example because I didn’t need it. The HATEOAS response should contain a link used for updating/removing/querying a specific item, so you could use a similar approach as the remove functionality.

      I’m not a HATEOAS expert, but I’m pretty sure you can’t. After writing this article, my opinion is that I don’t really like it, because it increases the workload on the front-end a lot. Because it follows the Representational State Transfer (REST) guidelines thoroughly, it means that you only know how to update/delete/find a specific item is by querying the REST resource first so the response tells you how to do it.
      If you look at my more recent tutorial about Spring Boot (http://g00glen00b.be/prototyping-spring-boot-angularjs/) you will see that I’m no longer using Spring Data REST, but I’m using Spring Data JPA with a simple Spring Web MVC controller that returns an array of items. It requires some additional code on the back-end, but makes the front-end a lot easier because I’m no longer using HATEOAS.

      • Aycan Adal

        I’ve been messing around with consuming spring data rest api from angular for three days now. I started with your article because it’s the only example out there nicely structured and coded. But it turns out that what you do here (crud) is totally doable and much cleaner with plain $resource. You don’t even need SpringDataRestAdapter for this. And yes SpringDataRestAdapter does provide means for doing crud out of the box as well but the real strength is how it helps you traverse links not only as in crud actions but as in related resources like posts of a user and complex actions that fall out of crud’s scope. And it’s fine tuned for spring data rest repositories. Out of angular-hateoas, restangular and other alternatives, SpringDataRestAdapter is definitely the best choice against a spring backend. So what you show here is pretty good for bigger scale applications but is an overkill for a project like this and for beginners like me. Check this: http://blog.mgechev.com/2014/02/05/angularjs-resource-active-record-http/

        The equivalent of your item factory looks like:

        module.factory(‘User’, function ($resource) {
        return $resource(‘/users/:id’);
        });

        Three lines of code 🙂 And you can create(instantiate), get, query, update, delete and do much more just like you do in your controller using the prototype returned by $resource. You can also post a new resource without having to load all the resources first.

        • True, I know how to use

          $resource

          . I’m using it for most of my blog articles as well. But for Spring-Data you have to do some modifications. For example, the

          query()

          method will not work with Spring Data (by default), because Angular-resource expects an array, while (if I recall correctly), Spring HATEOAS returns an object with an embedded array.

  • sd

    Hi – I downloaded and attempted to run the app (using NetBeans v8.0.2, Oracle WebLogic 12c). My IE11 Browser URL resolves to http://localhost:7001 (default) and page displays a “Error 403–Forbidden” message… – I tried manually typing in http://localhost:7001/index.jsp, but, same thing. Can you venture a couple of possibilities as to what I might be doing wrong? (thanks for your example in any case)

    • Are you sure the context root of your application is

      /

      ? In Eclipse it’s usually the name of the application. If you’re using the code from the Github repository, it should be http://localhost:7001/ng-spring-data/

    • johaness vix

      it cannot work because its incomplete!

  • Greg Turnquist

    Looks great! FYI @RepositoryRestResource(collectionResourceRel = “items”, path = “items”) is unnecessary. Spring Data REST takes the name of your domain class, and using the EvoInflector library, creates a pluralized version of it, which in this case, would be “items”. Only if you want to change that choice do you need this annotation (like Person => “people”).
    BTW, have you looked at Spring Boot? With that, you can really reduce your Spring footprint and setup costs. it lets you move to the Angular part really fast. If you put spring-boot-starter-data-rest, spring-boot-data-jpa, and spring-boot-starter-web in your pom.xml, then you an drop your JS libs into src/main/resources/static, and be off the Angular!
    Anyway, cheers on the blog entry!