Categories: springmvc

Spring 4 MVC+AngularJS Routing Example using UI-Router

This post shows integrating AngularJS with Spring MVC 4, focusing on routing using ui.router module. We will discuss ui.router based route configuration using states, $stateProvider, $urlRouterProvider, accessing route parameters using $stateParams, discuss route configuration details with template, templateUrl, controller & resolve. Let’s get going.


In our application, Client side is based on AngularJS ui.router, demonstrating route configuration using states. Server side is based on Spring , serving HTML using velocity templates and data using Spring REST API. This is what our final Application will look like:

To learn about AngularJS Routing using UI-Router in general, you can refer to AngularJS Routing Tutorial using UI-Router.

Let’s get started.

Following technologies being used:

  • Spring 4.2.0.RELEASE
  • AngularJS 1.4.4
  • Maven 3
  • JDK 1.7
  • Eclipse Mars.1 Release (4.5.1)

Project Structure


Provide Dependencies in pom.xml

<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.websystique.springmvc</groupId>
 <artifactId>Spring4MVCAngularJSRoutingWithUIRouterExample</artifactId>
 <packaging>war</packaging>
 <version>1.0.0</version>
 <name>Spring4MVCAngularJSRoutingWithUIRouterExample Maven Webapp</name>

 <properties>
  <springframework.version>4.2.0.RELEASE</springframework.version>
  <jackson.version>2.5.3</jackson.version>
 </properties>

 <dependencies>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${springframework.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context-support</artifactId>
   <version>${springframework.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-tx</artifactId>
   <version>${springframework.version}</version>
  </dependency>
  <dependency>
   <groupId>org.apache.velocity</groupId>
   <artifactId>velocity</artifactId>
   <version>1.7</version>
  </dependency>

  <dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>${jackson.version}</version>
  </dependency>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>3.1.0</version>
  </dependency>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>jstl</artifactId>
   <version>1.2</version>
  </dependency>
  <dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
   <version>3.4</version>
  </dependency>
 </dependencies>


 <build>
  <pluginManagement>
   <plugins>
    <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-compiler-plugin</artifactId>
     <version>3.2</version>
     <configuration>
      <source>1.7</source>
      <target>1.7</target>
     </configuration>
    </plugin>
    <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-war-plugin</artifactId>
     <version>2.4</version>
     <configuration>
      <warSourceDirectory>src/main/webapp</warSourceDirectory>
      <warName>Spring4MVCAngularJSRoutingWithUIRouterExample</warName>
      <failOnMissingWebXml>false</failOnMissingWebXml>
     </configuration>
    </plugin>
   </plugins>
  </pluginManagement>
  <finalName>Spring4MVCAngularJSRoutingWithUIRouterExample</finalName>
 </build>
</project>

1. Client Side

Client side of our Application is AngularJS based. If you would like to add AngularJs in your skill set, Detailed AngularJS Tutorial is available for you to deep dive in one of the most popular Javascript Meta framwework.

Route configuration:

Configure routes using AngularUI ui.router module. AngularUI Router is a routing framework for AngularJS, which allows you to organize the parts of your interface into a state machine. Unlike the $route service in the Angular ngRoute module, which is organized around URL routes, UI-Router is organized around states, which may optionally have routes, as well as other behavior attached.

Route configuration is done in AngularJS module’s config function, using $stateProvider service.

app.js

'use strict';

var App = angular.module('myApp',['ui.router']);

App.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider){
 
 $urlRouterProvider.otherwise("/category")
 
 $stateProvider
 .state('category', {
  url: "/category",
  templateUrl: 'category',
  controller : "CategoryController as ctgController",
  resolve: {
            async: ['ItemService', function(ItemService) {
                return ItemService.fetchCategoryList();
            }]
        }
 })

 .state('category.list', {
  url: '/{categoryId:[A-Za-z]{0,9}}',
  templateUrl: function(params){ return 'category/' + params.categoryId; },
  controller : "ItemListController as itemListCtrl",
  resolve: {
            async: ['ItemService', '$stateParams', function(ItemService, $stateParams) {
                return ItemService.fetchAllItems($stateParams.categoryId);
            }]
        }
 })

 .state('category.list.detail', {
  url: '/{itemId:[0-9]{1,9}}',
  templateUrl: function(params){ return 'category/' + params.categoryId +'/'+params.itemId; },
  controller : "ItemDetailsController as itemDetailsCtrl",
  resolve: {
            async: ['ItemService', '$stateParams', function(ItemService, $stateParams) {
                return ItemService.fetchSpecificItem($stateParams.categoryId, $stateParams.itemId);
            }]
        }
 })

}]);

Our example demonstrates Nested States using ui.router. In this example, we have one top-level state “category” and two nested states “category.list” and “category.list.detail”. Note that parent state of “category.list” is “category” [look at dot in the name]. Similarly, “category.list” is the parent state of “category.list.detail”.

Main Highlights:

  • The $urlRouterProvider provides us the way to configure a default URL.
  • The $stateProvider provides us state(name,object) functions in order to define all states in one place. First argument is the name of state and second argument is an object which eventually may contain url/template/templateUrl/Controller/resolve and other functions.
  • States category.list & category.list.details are using regular expressions to match specific URL which will eventually include paramters [catagoryId , itemId]. We can get those parameters using $stateParams and then eventually pass them to server to get the specific content.
  • Look specially at how the resolve is used to trigger service for communicating servers. And eventually the promise(named ‘async’ above) is passed into controllers to fetch the category or specific item details.

For more in depth details, please refer to Angularjs Routing Tutorial using ui-router.

Service Creation:

Configure Service which will eventually communicate with our Spring based server. For any doubts on service setup, please refer to AngularJS Services Tutorial.

ItemService.js

'use strict';

App.factory('ItemService', ['$http', '$q', function($http, $q){

 return {
  
   fetchCategoryList: function() {//Fetches category list from server.
    return $http.get('http://localhost:8080/Spring4MVCAngularJSRoutingWithUIRouterExample/categories')
     .then(
        function(response){
         return response.data;
        }, 
        function(errResponse){
         console.error('Error while fetching Items');
         return $q.reject(errResponse);
        }
      );
   },

   fetchAllItems: function(category) {//Fetches list of item for a specific category.
     return $http.get('http://localhost:8080/Spring4MVCAngularJSRoutingWithUIRouterExample/item/'+category)
       .then(
         function(response){
          return response.data;
         }, 
         function(errResponse){
          console.error('Error while fetching Items');
          return $q.reject(errResponse);
         }
       );
   },
      
   fetchSpecificItem: function(category,id) {//Fetches a specific item based on category and item id.
    return $http.get('http://localhost:8080/Spring4MVCAngularJSRoutingWithUIRouterExample/item/'+category+'/'+id)
      .then(
        function(response){
         return response.data;
        }, 
        function(errResponse){
         console.error('Error while fetching specific Item');
         return $q.reject(errResponse);
        }
      );
   }
 };

}]);

Controller Creation:

Create controller for Item lists and details.
CategoryController.js : Used to display list of categories [on the home page].

'use strict';

App.controller('CategoryController', ['async', function(async) {
          var self = this;
          self.categories=async;
}]);

ItemListController.js : Used to display list of items.

'use strict';

App.controller('ItemListController', ['async', function(async) {
          var self = this;
          self.items=async;
}]);

ItemDetailsController.js : Used to display details of a specific item.

'use strict';

App.controller('ItemDetailsController', ['async', function(async) {
          var self = this;
          self.item=async;
}]);

In all above controller, notice how we are passing the Promise ‘async’ from service into controller. For any doubts on controller setup, please refer to AngularJS Controllers Tutorial.

Configure Welcome Page:

Shown below is the main page, containing only ui-viw directive.

index.html

<!doctype html>
<html lang="en" ng-app="myApp">
<head>

    <meta charset="utf-8">
    <title>Service App</title>
    <link rel="stylesheet" href="resources/css/bootstrap.css" />
    <link rel="stylesheet" href="resources/css/app.css" />
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js"></script>
    <script src="http://angular-ui.github.io/ui-router/release/angular-ui-router.js"></script>
 <script src="resources/js/app.js"></script>
 <script src="resources/js/service/ItemService.js"></script>
 <script src="resources/js/controller/CategoryController.js"></script>
 <script src="resources/js/controller/ItemListController.js"></script>
 <script src="resources/js/controller/ItemDetailsController.js"></script>
</head>
<body>
 <div ui-view></div>
</body>
</html>

On application startup, config function will be invoked which will eventually set the default state to ‘category’ [thanks to $urlRouterProvider.otherwise(“/category”) ], and will activate the category state. Below is the peace of code [from app.js] responsible for this:

$urlRouterProvider.otherwise("/category")
 
 $stateProvider
 .state('category', {
  url: "/category",
  templateUrl: 'category',
  controller : "CategoryController as ctgController",
  resolve: {
            async: ['ItemService', function(ItemService) {
                return ItemService.fetchCategoryList();
            }]
        }
 })

resolve will be called on this default state, which will eventually send a request to server [using service] to get the category-list. The promise from service [async] will be passed to controller which will be applied on templates of this state.

Template for Category state is shown below:
all_categories.html

 <div class="generic-container">
  <h2>SellPoint - AngularJS UI-Router based Routing Application</h2>
  <div class="panel panel-default">
   <!-- Default panel contents -->
      <div class="panel-heading"><span class="lead">All Categories</span></div>
      <div class="tablecontainer">
    <ul>
      <li ng-repeat="category in ctgController.categories">{{item}}
        <a ui-sref="category.list({categoryId:category})">{{category}}</a>
      </li>
    </ul>
   </div>
  </div>
 
  <div ui-view></div>
 </div>

Main Highlights:

  • Notice that hyperlink for the Categories uses ui-sref instead of regular href. ui-sref is a directive which in addition to auto-generating href attribute, binds a link [<a> tag] to a state. Clicking the link will trigger a state transition with optional parameters. In above excerpt, we have said that clicking the link will trigger “category.list” state.
  • A state with a dot in it’s name is considered as Nested State. In this case we say the category.list is a nested state of “category” state.
  • When a state is activated [by clicking on ui-sref hyperlink or directly typing the URL (mentioned in state configuration) in browser], its templates are automatically inserted into the ui-view of its parent state’s template. If it’s a top-level state—which ‘category’ is because it has no parent state–then its parent template is index.html. So in this case, this template will be inserted into ui-view defined in index.html.
  • We can also pass parameters to state using {parameterName:value} form. In this example, we pass categoryId as parameter using {categoryId:category} which refers to specific category. This parameter can then be accessed using $stateParams, and used to perform further logic.

Create Templates for Nested states:

category_computers.html

 <div class="panel panel-default">
  <!-- Default panel contents -->
     <div class="panel-heading"><span class="lead">List of Computers</span></div>
     <div class="tablecontainer">
      <table class="table table-hover">
             <thead>
                 <tr>
                     <th>ID</th>
                     <th>Brand</th>
                     <th>Model</th>
                     <th>Price (EUR)</th>
                 </tr>
             </thead>
             <tbody>
                 <tr ng-repeat="item in itemListCtrl.items">
                     <td>{{item.id}}</td>
                     <td>{{item.brand}}</td>
                     <td><a ui-sref="category.list.detail({categoryId:'Computers',itemId:item.id})">{{item.model}}</a></td>
                     <td>{{item.price}}</td>
                 </tr>
             </tbody>
   </table>
  </div>
 </div>
 
 <div ui-view></div>

Main Highlights:

  • This templates is similar to the category template [all_categories.html] & it corresponds to nested state [category.list], & gets activated when clicking on ui-sref links in all_categories.html. Since category.list is a nested state, it’s template [this one] will be injected in ui-view of parent [category state : inside all_categories.html]
  • This template contains ui-sref which refers to it’s child state ‘category.list.detail’
  • We are also passing two parameters to the child state :categoryId we got from parent state category & item Id for a specific item
  • This template itself contains ui-view directive, which means further nested template [child of this state] can be injected here.

To recall, following is the excerpt from app.js related to this state:

 .state('category.list', {
  url: '/{categoryId:[A-Za-z]{0,9}}',
  templateUrl: function(params){ return 'category/' + params.categoryId; },
  controller : "ItemListController as itemListCtrl",
  resolve: {
            async: ['ItemService', '$stateParams', function(ItemService, $stateParams) {
                return ItemService.fetchAllItems($stateParams.categoryId);
            }]
        }
 })

As we saw before, the categoryId parameter is passed from category state [from within all_categories.html]. We access this parameter here using $stateParams and send that parameter’s value to server to fetch the list of items for that specific category. Then we pass the Promise of that request [‘async’] to controller which eventually use it to show details on template for list page.

Additionally, look at how we have used the state parameter to create specific request URL to send to server to fetch specific template. AngularJS let’s us do that by passing $stateParams as a parameter in a function within which we can access it to do eventually use the parameters. Nice.

category_computers_detail.html

 <div class="panel panel-default">
  <!-- Default panel contents -->
     <div class="panel-heading"><span class="lead">Computer Details</span></div>
     <div class="tablecontainer">
      <table class="table table-hover">
    <tr>
                    <th>ID</th><td>{{itemDetailsCtrl.item.id}}</td><th>Brand</th><td>{{itemDetailsCtrl.item.brand}}</td>
                </tr>
                <tr>
                    <th>Model</th><td>{{itemDetailsCtrl.item.model}}</td><th>State</th><td>{{itemDetailsCtrl.item.state}}</td>
                </tr>
                <tr>
                    <th>Price (EUR)</th><td>{{itemDetailsCtrl.item.price}}</td><th>Processor (GHZ)</th><td>{{itemDetailsCtrl.item.processorSpeed}}</td>
                </tr>
                <tr>
                    <th>RAM (GB)</th><td>{{itemDetailsCtrl.item.ram}}</td><th>Disk Capacity</th><td>{{itemDetailsCtrl.item.diskCapacity}}</td>
                </tr>
   </table>
  </div>
 </div>

This is the template for ‘category.list.detail’ state which is the child state of ‘category.list’. That means it will be injected in ui-view of it’s parent template which is category_computers.html. To recall, following is the excerpt from app.js related to this state:

 .state('category.list.detail', {
  url: '/{itemId:[0-9]{1,9}}',
  templateUrl: function(params){ return 'category/' + params.categoryId +'/'+params.itemId; },
  controller : "ItemDetailsController as itemDetailsCtrl",
  resolve: {
            async: ['ItemService', '$stateParams', function(ItemService, $stateParams) {
                return ItemService.fetchSpecificItem($stateParams.categoryId, $stateParams.itemId);
            }]
        }
 })

We are accessing the parameters passed from parent state using $stateParams, and then sending there values with request to server to get the detail of a specific item which belongs to a specific category.

That’s all for client side. To complete, shown below are remaining templates used in this application.

category_phones.html

 <div class="panel panel-default">
  <!-- Default panel contents -->
     <div class="panel-heading"><span class="lead">List of Phones</span></div>
     <div class="tablecontainer">
      <table class="table table-hover">
             <thead>
                 <tr>
                     <th>ID</th>
                     <th>Brand</th>
                     <th>Model</th>
                     <th>Price (EUR)</th>
                 </tr>
             </thead>
             <tbody>
                 <tr ng-repeat="item in itemListCtrl.items">
                     <td>{{item.id}}</td>
                     <td>{{item.brand}}</td>
                        <td><a ui-sref="category.list.detail({categoryId:'Phones',itemId:item.id})">{{item.model}}</a></td>
                     <td>{{item.price}}</td>
                 </tr>
             </tbody>
   </table>
  </div>
 </div>
 <div ui-view></div>

category_phones_detail.html

 <div class="panel panel-default">
  <!-- Default panel contents -->
     <div class="panel-heading"><span class="lead">Phone Details</span></div>
     <div class="tablecontainer">
      <table class="table table-hover">
    <tr>
                    <th>ID</th><td>{{itemDetailsCtrl.item.id}}</td><th>Brand</th><td>{{itemDetailsCtrl.item.brand}}</td>
                </tr>
                <tr>
                    <th>Model</th><td>{{itemDetailsCtrl.item.model}}</td><th>State</th><td>{{itemDetailsCtrl.item.state}}</td>
                </tr>
                <tr>
                    <th>Price (EUR)</th><td>{{itemDetailsCtrl.item.price}}</td><th>Size</th><td>{{itemDetailsCtrl.item.size}}</td>
                </tr>
                <tr>
                    <th>Camera (MB)</th><td>{{itemDetailsCtrl.item.camera}}</td><th>SD Card (GB)</th><td>{{itemDetailsCtrl.item.sdCard}}</td>
                </tr>
                <tr>
                    <th>Replaceable Battery</th><td>{{itemDetailsCtrl.item.replacableBattery}}</td><th></th><td></td>
    </tr>
   </table>
  </div>
 </div>

category_printers.html

 <div class="panel panel-default">
  <!-- Default panel contents -->
     <div class="panel-heading"><span class="lead">List of Printers</span></div>
     <div class="tablecontainer">
      <table class="table table-hover">
             <thead>
                 <tr>
                     <th>ID</th>
                     <th>Brand</th>
                     <th>Model</th>
                     <th>Price (EUR)</th>
                 </tr>
             </thead>
             <tbody>
                 <tr ng-repeat="item in itemListCtrl.items">
                     <td>{{item.id}}</td>
                     <td>{{item.brand}}</td>
                        <td><a ui-sref="category.list.detail({categoryId:'Printers',itemId:item.id})">{{item.model}}</a></td>
                     <td>{{item.price}}</td>
                 </tr>
             </tbody>
   </table>
  </div>
 </div>
 <div ui-view></div>

category_printers_detail.html

 <div class="panel panel-default">
  <!-- Default panel contents -->
     <div class="panel-heading"><span class="lead">Printer Details</span></div>
     <div class="tablecontainer">
      <table class="table table-hover">
    <tr>
                    <th>ID</th><td>{{itemDetailsCtrl.item.id}}</td><th>Brand</th><td>{{itemDetailsCtrl.item.brand}}</td>
                </tr>
                <tr>
                    <th>Model</th><td>{{itemDetailsCtrl.item.model}}</td><th>State</th><td>{{itemDetailsCtrl.item.state}}</td>
                </tr>
                <tr>
                    <th>Price (EUR)</th><td>{{itemDetailsCtrl.item.price}}</td><th>Technology</th><td>{{itemDetailsCtrl.item.technologie}}</td>
                </tr>
                <tr>
                    <th>Paper Format</th><td>{{itemDetailsCtrl.item.paperFormat}}</td><th>PPM</th><td>{{itemDetailsCtrl.item.pagesPerMinute}}</td>
                </tr>
   </table>
  </div>
 </div>

2. Server Side

Spring Configuration Class:

HelloWorldConfiguration.java

package com.websystique.springmvc.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.velocity.VelocityConfigurer;
import org.springframework.web.servlet.view.velocity.VelocityView;
import org.springframework.web.servlet.view.velocity.VelocityViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.websystique.springmvc")
public class HelloWorldConfiguration extends WebMvcConfigurerAdapter{
 
 
 @Bean
 public VelocityConfigurer velocityConfig() {
     VelocityConfigurer velocityConfigurer = new VelocityConfigurer();
     velocityConfigurer.setResourceLoaderPath("/WEB-INF/velocity/");
     return velocityConfigurer;
 }
 
 @Override
 public void configureViewResolvers(ViewResolverRegistry registry) {
  VelocityViewResolver viewResolver = new VelocityViewResolver();
  
  viewResolver.setViewClass(VelocityView.class);
  viewResolver.setCache(true);
  viewResolver.setPrefix("");
  viewResolver.setSuffix(".html");
  viewResolver.setExposeSpringMacroHelpers(true);

  registry.viewResolver(viewResolver);
 }

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

}

Create WelcomePage controller

IndexController.java

package com.websystique.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/")
public class IndexController {//Serves main index.html

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

Create Template Controller handling the requests for templates:

TemplateController.java

package com.websystique.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class TemplateController {//Serves Templates.

    @RequestMapping(value="/category")
    public String getMainTemplate() {
     return "template/all_categories"; 
    }

    @RequestMapping(value="/category/Computers")
    public String getComputersTemplate() {
     return "template/category_computers"; 
    }

    @RequestMapping(value="/category/Phones")
    public String getPhonesTemplate() {
     return "template/category_phones"; 
    }

    @RequestMapping(value="/category/Printers")
    public String getPrintersTemplate() {
     return "template/category_printers"; 
    }

    @RequestMapping(value="/category/Computers/{id}")
    public String getComputersDetailTemplate(@PathVariable("id") long id) {
     return "template/category_computers_detail"; 
    }

    @RequestMapping(value="/category/Phones/{id}")
    public String getPhonesDetailTemplate(@PathVariable("id") long id) {
     return "template/category_phones_detail"; 
    }

    @RequestMapping(value="/category/Printers/{id}")
    public String getPrintersDetailTemplate(@PathVariable("id") long id) {
     return "template/category_printers_detail"; 
    }

}

Create REST Controller handling the requests for list and detail of items:

ItemController.java

package com.websystique.springmvc.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import com.websystique.springmvc.service.ItemService;

@Controller
public class ItemController {//Serves Data.

    @Autowired
    ItemService itemService;  //Service which will do all data retrieval/manipulation work
 
    @RequestMapping("/categories")
    public ResponseEntity<List> listAllCategories() {
  System.out.println("*************************************ListAllItems");
     List<String> categories =  itemService.populateAllCategories();
        if(categories.isEmpty()){
            return new ResponseEntity<List>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
        }
        return new ResponseEntity<List>(categories, HttpStatus.OK);
    }

 @RequestMapping(value="/item/Computers")
    public ResponseEntity<List> listAllComputers() {
  System.out.println("*************************************ListAllComputers");
     List items =  itemService.findItemsByCategory("computers");
        if(items.isEmpty()){
            return new ResponseEntity<List>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
        }
        return new ResponseEntity<List>(items, HttpStatus.OK);
    }

 @RequestMapping(value="/item/Computers/{id}")
    public ResponseEntity<Object> findSpecificComputer(@PathVariable("id") long id) {
  System.out.println("*************************************findSpecificComputer");
     Object item =  itemService.findItemById(id, "computers");
        if(item == null){
            return new ResponseEntity<Object>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
        }
        return new ResponseEntity<Object>(item, HttpStatus.OK);
    }

 
 @RequestMapping(value="/item/Phones")
    public ResponseEntity<List> listAllPhones() {
  System.out.println("*************************************ListAllPhones");
     List items =  itemService.findItemsByCategory("phones");
        if(items.isEmpty()){
            return new ResponseEntity<List>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
        }
        return new ResponseEntity<List>(items, HttpStatus.OK);
    }

 @RequestMapping(value="/item/Phones/{id}")
    public ResponseEntity<Object> findSpecificPhone(@PathVariable("id") long id) {
  System.out.println("*************************************findSpecificPhone");
     Object item =  itemService.findItemById(id, "phones");
        if(item == null){
            return new ResponseEntity<Object>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
        }
        return new ResponseEntity<Object>(item, HttpStatus.OK);
    }

 @RequestMapping(value="/item/Printers")
    public ResponseEntity<List> listAllPrinters() {
  System.out.println("*************************************ListAllPrinters");
     List items =  itemService.findItemsByCategory("printers");
        if(items.isEmpty()){
            return new ResponseEntity<List>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
        }
        return new ResponseEntity<List>(items, HttpStatus.OK);
    }

 @RequestMapping(value="/item/Printers/{id}")
    public ResponseEntity<Object> findSpecificPrinter(@PathVariable("id") long id) {
  System.out.println("*************************************findSpecificPrinter");
     Object item =  itemService.findItemById(id, "printers");
        if(item == null){
            return new ResponseEntity<Object>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
        }
        return new ResponseEntity<Object>(item, HttpStatus.OK);
    }

}

Services for providing dummy data for item lists and details:

ItemService.java

package com.websystique.springmvc.service;

import java.util.List;

public interface ItemService {

 List findItemsByCategory(String category);
 
 Object findItemById(long id, String category);
 
 List<String> populateAllCategories();
}

ItemServiceImpl.java

package com.websystique.springmvc.service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.websystique.springmvc.model.Computer;
import com.websystique.springmvc.model.Phone;
import com.websystique.springmvc.model.Printer;
import com.websystique.springmvc.model.State;

@Service("itemService")
public class ItemServiceImpl implements ItemService{
 
 private static final AtomicLong counter = new AtomicLong();

 private static List<Computer> computers;
 
 private static List<Phone> phones;
 
 private static List<Printer> printers;
 
 
 static{
  computers= populateDummyComputers();
  phones= populateDummyPhones();
  printers= populateDummyPrinters();
  
 }
 
 public List findItemsByCategory(String category) {

  if(StringUtils.equalsIgnoreCase(category, "computers")){
    return computers;
  } else if(StringUtils.equalsIgnoreCase(category, "phones")){
   return phones;
  } else if(StringUtils.equalsIgnoreCase(category, "printers")){
   return printers;
  }
  return computers;
 }

 public Object findItemById(long id, String category) {
  if(category.equalsIgnoreCase("computers")){
   for(Computer computer : computers){
    if(computer.getId() == id){
     return computer;
    }
   }
  }else if(category.equalsIgnoreCase("phones")){
   for(Phone phone : phones){
    if(phone.getId() == id){
     return phone;
    }
   }
  } if(category.equalsIgnoreCase("printers")){
   for(Printer printer : printers){
    if(printer.getId() == id){
     return printer;
    }
   }
   
  }
  return null;
 }
 

 public List<String> populateAllCategories(){
  List<String> allCategories = new ArrayList<String>();
  allCategories.add("Computers");
  allCategories.add("Phones");
  allCategories.add("Printers");
  return allCategories;
 }

 
 private static List<Computer> populateDummyComputers(){
  List<Computer> computers = new ArrayList<Computer>();
  
  computers.add(new Computer(counter.incrementAndGet(),"Lenovo", "T420", State.NEW, 500, 2.4, 8, 1000));
  computers.add(new Computer(counter.incrementAndGet(),"Lenovo", "T640", State.NEW, 2000, 3.6, 32, 2000));
  computers.add(new Computer(counter.incrementAndGet(),"Apple", "IMAC21.5", State.NEW, 1400, 2.6, 8, 1000));
  computers.add(new Computer(counter.incrementAndGet(),"HP", "Pavilion", State.NEW, 900, 2.4, 8, 1000));
  computers.add(new Computer(counter.incrementAndGet(),"Dell", "E6510", State.NEW, 500, 2.3, 8, 500));
  return computers;
 }

 private static List<Phone> populateDummyPhones(){
  List<Phone> phones = new ArrayList<Phone>();
  
  phones.add(new Phone(counter.incrementAndGet(),"Apple", "IPhone6s", State.NEW, 800, 5.2, false,false,15));
  phones.add(new Phone(counter.incrementAndGet(),"Huwawi", "Nexus6P", State.NEW, 700, 5.7, false,false,13));
  phones.add(new Phone(counter.incrementAndGet(),"Samsung", "Note5", State.NEW, 600, 5.7, false,false,14));
  phones.add(new Phone(counter.incrementAndGet(),"HTC", "M9", State.NEW, 580, 5.5, true,true,16));
  phones.add(new Phone(counter.incrementAndGet(),"LG", "G4", State.NEW, 550, 5.7, true,true,15));
  return phones;
 }


 private static List<Printer> populateDummyPrinters(){
  List<Printer> printers = new ArrayList<Printer>();
  
  printers.add(new Printer(counter.incrementAndGet(),"HP", "OfficeJet 7500A", State.NEW, 200, "A4", "InkJet",15));
  printers.add(new Printer(counter.incrementAndGet(),"Brother", "J6520dw", State.NEW, 180, "A4", "InkJet",12));
  printers.add(new Printer(counter.incrementAndGet(),"EPSON", "XP-820", State.NEW, 210, "A4", "InkJet",14));
  return printers;
 }

}

Model Classes:

Computer.java

package com.websystique.springmvc.model;

public class Computer{

 private long id;
 
 private String brand;
 
 private String model;
 
 private State state;
 
 private double price;

 private double processorSpeed;
 
 private int ram;
 
 private int diskCapacity;


 public Computer(long id,String brand, String model, State state, double price, 
   double processorSpeed, int ram, int diskCapacity){
  this.id = id;
  this.brand = brand;
  this.model = model;
  this.state = state;
  this.price = price;
  this.processorSpeed = processorSpeed;
  this.ram =ram;
  this.diskCapacity = diskCapacity;
 }
 
 public long getId() {
  return id;
 }

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

 public String getBrand() {
  return brand;
 }

 public void setBrand(String brand) {
  this.brand = brand;
 }

 public String getModel() {
  return model;
 }

 public void setModel(String model) {
  this.model = model;
 }

 public State getState() {
  return state;
 }

 public void setState(State state) {
  this.state = state;
 }

 public double getPrice() {
  return price;
 }

 public void setPrice(double price) {
  this.price = price;
 }

 
 public double getProcessorSpeed() {
  return processorSpeed;
 }

 public void setProcessorSpeed(double processorSpeed) {
  this.processorSpeed = processorSpeed;
 }

 public int getRam() {
  return ram;
 }

 public void setRam(int ram) {
  this.ram = ram;
 }

 public int getDiskCapacity() {
  return diskCapacity;
 }

 public void setDiskCapacity(int diskCapacity) {
  this.diskCapacity = diskCapacity;
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + (int) (id ^ (id >>> 32));
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Computer other = (Computer) obj;
  if (id != other.id)
   return false;
  return true;
 }

 @Override
 public String toString() {
  return "Computer [id=" + id + ", brand=" + brand + ", model=" + model + ", state=" + state + ", price=" + price
    + ", processorSpeed=" + processorSpeed + ", ram=" + ram + ", diskCapacity=" + diskCapacity + "]";
 }
}

Phone.java

package com.websystique.springmvc.model;

public class Phone{
 
 private long id;
 
 private String brand;
 
 private String model;
 
 private State state;
 
 private double price;

 private double size;
 
 private boolean sdCard;
 
 private boolean replacableBattery;
 
 private int camera;
 
 public Phone(long id,String brand, String model, State state, double price, 
   double size, boolean sdCard, boolean replacableBattery,int camera){
  this.id = id;
  this.brand = brand;
  this.model = model;
  this.state = state;
  this.price = price;
  this.size = size;
  this.sdCard =sdCard;
  this.replacableBattery = replacableBattery;
  this.camera = camera;
 }

 public long getId() {
  return id;
 }

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

 public String getBrand() {
  return brand;
 }

 public void setBrand(String brand) {
  this.brand = brand;
 }

 public String getModel() {
  return model;
 }

 public void setModel(String model) {
  this.model = model;
 }

 public State getState() {
  return state;
 }

 public void setState(State state) {
  this.state = state;
 }

 public double getPrice() {
  return price;
 }

 public void setPrice(double price) {
  this.price = price;
 }

 public double getSize() {
  return size;
 }

 public void setSize(double size) {
  this.size = size;
 }

 public boolean isSdCard() {
  return sdCard;
 }

 public void setSdCard(boolean sdCard) {
  this.sdCard = sdCard;
 }

 public boolean isReplacableBattery() {
  return replacableBattery;
 }

 public void setReplacableBattery(boolean replacableBattery) {
  this.replacableBattery = replacableBattery;
 }

 public int getCamera() {
  return camera;
 }

 public void setCamera(int camera) {
  this.camera = camera;
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + (int) (id ^ (id >>> 32));
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Phone other = (Phone) obj;
  if (id != other.id)
   return false;
  return true;
 }

 @Override
 public String toString() {
  return "Phone [id=" + id + ", brand=" + brand + ", model=" + model + ", state=" + state + ", price=" + price
    + ", size=" + size + ", sdCard=" + sdCard + ", replacableBattery=" + replacableBattery + ", camera="
    + camera + "]";
 }

 
 
}

Printer.java

package com.websystique.springmvc.model;

public class Printer{
 
 private long id;
 
 private String brand;
 
 private String model;
 
 private State state;
 
 private double price;

 private String paperFormat;
 
 private String technologie;
 
 private int pagesPerMinute;
 
 public Printer(long id,String brand, String model, State state, double price, 
   String paperFormat, String technologie, int pagesPerMinute){
  this.id = id;
  this.brand = brand;
  this.model = model;
  this.state = state;
  this.price = price;
  this.paperFormat = paperFormat;
  this.technologie =technologie;
  this.pagesPerMinute = pagesPerMinute;
 }

 public long getId() {
  return id;
 }

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

 public String getBrand() {
  return brand;
 }

 public void setBrand(String brand) {
  this.brand = brand;
 }

 public String getModel() {
  return model;
 }

 public void setModel(String model) {
  this.model = model;
 }

 public State getState() {
  return state;
 }

 public void setState(State state) {
  this.state = state;
 }

 public double getPrice() {
  return price;
 }

 public void setPrice(double price) {
  this.price = price;
 }

 public String getPaperFormat() {
  return paperFormat;
 }

 public void setPaperFormat(String paperFormat) {
  this.paperFormat = paperFormat;
 }

 public String getTechnologie() {
  return technologie;
 }

 public void setTechnologie(String technologie) {
  this.technologie = technologie;
 }

 public int getPagesPerMinute() {
  return pagesPerMinute;
 }

 public void setPagesPerMinute(int pagesPerMinute) {
  this.pagesPerMinute = pagesPerMinute;
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + (int) (id ^ (id >>> 32));
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Printer other = (Printer) obj;
  if (id != other.id)
   return false;
  return true;
 }

 @Override
 public String toString() {
  return "Printer [id=" + id + ", brand=" + brand + ", model=" + model + ", state=" + state + ", price=" + price
    + ", paperFormat=" + paperFormat + ", technologie=" + technologie + ", pagesPerMinute=" + pagesPerMinute
    + "]";
 }
 
}

State.java

package com.websystique.springmvc.model;

public enum State {
 NEW,USED;
}

CORS Filter: Creating Filter to handle Same Origin Policy related issues


package com.websystique.springmvc.configuration;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;


public class CORSFilter implements Filter {

 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
  HttpServletResponse response = (HttpServletResponse) res;
  response.setHeader("Access-Control-Allow-Origin", "*");
  response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
  response.setHeader("Access-Control-Max-Age", "3600");
  response.setHeader("Access-Control-Allow-Headers", "x-requested-with, Content-Type");
  chain.doFilter(req, res);
 }

 public void init(FilterConfig filterConfig) {}

 public void destroy() {}

}

Create Spring Initializer class

Look at how we are registering CORS filter with Spring configuration, which will help us to get away with Same Origin Policy issues.

package com.websystique.springmvc.configuration;

import javax.servlet.Filter;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class HelloWorldInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
 
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { HelloWorldConfiguration.class };
    }
  
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return null;
    }
  
    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
    
    @Override
    protected Filter[] getServletFilters() {
     Filter [] singleton = { new CORSFilter() };
     return singleton;
 }
 
}

Deploy & Run

Now build the war (either by eclipse as was mentioned in previous tutorials) or via maven command line( mvn clean install). Deploy the war to a Servlet 3.0 container . Since here i am using Tomcat, i will simply put this war file into tomcat webapps folder and click on startup.bat inside tomcat/bin directory.

Open browser and browse at localhost:8080/Spring4MVCAngularJSRoutingWithUIRouterExample/. it will be redirected to /category [look at $urlRouterProvider.otherwise(“/category”) part in $stateProvider setup]. Check the URL in browser.

In above interaction, A request was sent to server to fetch the list of categories & another request was send to server to fetch corresponding template. You can open the developer tools in your browser and check the request sent for each operation to server and response received. In this case we got the list of categories.

Now click on Computers link, a request is sent to server to get the list of items for category ‘computers’. Again, a seperate request is sent to fetch the respective template for that category. On response, you should see all computers list below main panel. You can see that URL ‘/category/Computers’ reflects current state. You can even manually change URL, hit enter, and see the respective response coming back.

Now click on specific computer. Again a request will be sent to fetch the details of an item based on itemId and category, and another request is sent to fetch corresponding item details template. URL ‘/category/Computers/2’ reflects it.

That’s it.

Download Source Code


References

View Comments

  • hello,
    Thanks for the example,
    But I have a problem running it with eclipse neon it shows nothing and the project is having a root error.
    Is there any solution for this.
    I use clean install to deploy the application with maven.

  • Hi Websystique

    i have a little problem with this implementations.
    when i access : /exemple#/list from the angularjs menu(nav-bar) i get the perfect response in the ui-view
    .... but when i use the following url localhost:8080/myApp/exemple/list ; i obtain the list in a html page; not in the ui-view,
    that's because the template controller return "exemple/list" ;
    can you help me !

  • If anyone had problems with loading and running the sample application (downloaded at the end of this wonderful tutorial), I suggest you doing the following:

    1) Import the downloaded project from the archive into your workspace.

    2) Create a new Maven project with webapp archetype (as described earlier here: http://websystique.com/maven/create-a-maven-web-project-with-eclipse/)

    3) Override the pom.xml of the new project with the one contained in the project you downloaded.

    4) Rename the src/main/resources folder of the new project for src/main/java.

    5) Copy the content of the src/main/java folder from the downloaded project into the new project.

    6) Delete the content of the webapp folder in the new project (within the 'Deployed Resources' folder).

    7) Copy the content of the webapp folder from the downloaded project into the new project (within the 'Deployed Resources' folder, of course the structure should be: 'Deployed Resources'/webapp/copied folders and files).

    8) Update the new project due to the Maven properties. (Right click on the new project name in the Project / Package Explorer View of Eclipse -> Maven -> Update Project..)

    9) Deploy the new project on the server.

    10) Enjoy.

    Note: I referred with the term 'new project' to the new Maven project (with webapp archetype) you created at the beginning; while with the term 'downloaded project' I refer to the one that is contained in the archive downloaded from the end of this tutorial.

    • Hi Ben,
      Why would you preform all these steps. It works as it is, just tried it. It's a standard mvn web project.

      • Hi websystique,

        That's because when I tried to deploy the downloaded project in my Eclipse EE Neon, deployed on the apache-tomcat-8.0.36 (through Eclipse) I got the same errors (did not see anything) as Kumar in his/her post below.

        The solution above worked for the ui-router example, but for the ngRoute example it did not work at all. (Neither did the original project I downloaded from the website.)

  • Hi websystique , Great job with the tutorials . Although I am having hardest time using ur example application on tomcat8 . Except the project in which view is .jsp file (Spring 4 MVC+AngularJS CRUD Application using ngResource) no other project (i.e. UIRouter or ngRoute) is working . It gets successfully deployed but doesn't show anything on that URL . I have tried on both windows(tomcat8) and linux(tomcat7). Please refer to below screenshots .

  • Hi it was excellent learning through your site, I do have one small issue with above app, I am getting error in index.html file while referring for static files, the JS files and CSS are not loading.

    • Hi Veera,

      Make sure you have configured the resource handler in Configuration class.
      public void addResourceHandlers(ResourceHandlerRegistry registry) {
      registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
      }

      Additionally, do note that the resources folder is inside webapp.

Share
Published by

Recent Posts

Spring Boot + AngularJS + Spring Data + JPA CRUD App Example

In this post we will be developing a full-blown CRUD application using Spring Boot, AngularJS, Spring Data, JPA/Hibernate and MySQL,…

7 years ago

Spring Boot Rest API Example

Spring Boot complements Spring REST support by providing default dependencies/converters out of the box. Writing RESTful services in Spring Boot…

7 years ago

Spring Boot WAR deployment example

Being able to start the application as standalone jar is great, but sometimes it might not be possible to run…

7 years ago

Spring Boot Introduction + hello world example

Spring framework has taken the software development industry by storm. Dependency Injection, rock solid MVC framework, Transaction management, messaging support,…

7 years ago

Secure Spring REST API using OAuth2

Let's secure our Spring REST API using OAuth2 this time, a simple guide showing what is required to secure a…

8 years ago

AngularJS+Spring Security using Basic Authentication

This post shows how an AngularJS application can consume a REST API which is secured with Basic authentication using Spring…

8 years ago