Spring 4 MVC+AngularJS Routing Example using ngRoute

This post shows integrating AngularJS with Spring MVC 4, focusing on routing using ngRoute module. We will discuss route configuration using $routeProvider, accessing route parameters using $routeParams, discuss route configuration details with template, templateUrl, controller, redirectTo & resolve keys.


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

Spring4MVCAngularJSRoutingExample_img1


Spring4MVCAngularJSRoutingExample_img2

If you have any doubts on routing in AngularJS, please refer to AngularJS Routing Tutorial to know the in-depth details.

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

Spring4MVCAngularJSRoutingExample_img00
Spring4MVCAngularJSRoutingExample_img01

Provide Dependencies

<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>Spring4MVCAngularJSRoutingExample</artifactId>
	<packaging>war</packaging>
	<version>1.0.0</version>
	<name>Spring4MVCAngularJSRoutingExample Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<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>Spring4MVCAngularJSRoutingExample</warName>
						<failOnMissingWebXml>false</failOnMissingWebXml>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
		<finalName>Spring4MVCAngularJSRoutingExample</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 AngularJS ngRoute module. Route configuration is done in AngularJS module’s config function, using $routeProvider service.
In our example, we have two types of routes : itemList routes & itemDetails routes.Both are shown below. Look specially at how the resolve is used to trigger service for communicating servers. And eventually the promise(named ‘async’ below) is passed into controllers to fetch the item lists and details. For more in depth details, please refer to AngularJS Routing Tutorial

app.js

'use strict';

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

App.config(['$routeProvider', function($routeProvider) {
	$routeProvider
		.when('/items/computers', {
			templateUrl: 'items/computers',
			controller : "ItemListController as itemListCtrl",
			resolve: {
                async: ['ItemService', function(ItemService) {
                    return ItemService.fetchAllItems('computers');
               	}]
            }
		})
		.when('/items/phones', {
			templateUrl: 'items/phones',
			controller : "ItemListController as itemListCtrl",
			resolve: {
                async: ['ItemService', function(ItemService) {
                    return ItemService.fetchAllItems('phones');
               	}]
            }
		})
		.when('/items/printers', {
			templateUrl: 'items/printers',
			controller : "ItemListController as itemListCtrl",
			resolve: {
                async: ['ItemService', function(ItemService) {
                    return ItemService.fetchAllItems('printers');
               	}]
            }
		})
		.when('/items/computerdetails/:id', {
			templateUrl: 'items/computerdetails',
			controller : "ItemDetailsController as itemDetailsCtrl",
			resolve: {
                async: ['ItemService','$route', function(ItemService , $route) {
                    return ItemService.fetchSpecificItem('computers',$route.current.params.id);
               	}]
            }
		})
		.when('/items/phonedetails/:id', {
			templateUrl: 'items/phonedetails',
			controller : "ItemDetailsController as itemDetailsCtrl",
			resolve: {
                async: ['ItemService','$route', function(ItemService , $route) {
                    return ItemService.fetchSpecificItem('phones',$route.current.params.id);
               	}]
            }
		})
		.when('/items/printerdetails/:id', {
			templateUrl: 'items/printerdetails',
			controller : "ItemDetailsController as itemDetailsCtrl",
			resolve: {
                async: ['ItemService','$route', function(ItemService , $route) {
                    return ItemService.fetchSpecificItem('printers',$route.current.params.id);
               	}]
            }
		})
		
		.otherwise({redirectTo:'/items/computers'});		
}]);

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 {
		
			fetchAllItems: function(category) {
					return $http.get('http://localhost:8080/Spring4MVCAngularJSRoutingExample/item/'+category)
							.then(
									function(response){
										return response.data;
									}, 
									function(errResponse){
										console.error('Error while fetching Items');
										return $q.reject(errResponse);
									}
							);
			},
		    
			fetchSpecificItem: function(category,id) {
				return $http.get('http://localhost:8080/Spring4MVCAngularJSRoutingExample/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. For any doubts on service setup, please refer to AngularJS Controllers Tutorial.

Item List controller is shown below:
ItemListController.js

'use strict';

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

Item Details controller is shown below:
ItemDetailsController.js

'use strict';

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

Configure Welcome Page:

Shown below is the main page, containings URL’s to routes for lists, and ng-viw component which will eventually be filled via select routes template.

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="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular-route.js"></script>
    <script src="resources/js/app.js"></script>
    <script src="resources/js/service/ItemService.js"></script>
</head>
<body>
		
	<div class="generic-container">
		<h2>SellPoint - AngularJS Routing Application</h2>
		<div class="panel panel-default">
			<!-- Default panel contents -->
		    <div class="panel-heading"><span class="lead">All Items</span></div>
		    <div class="tablecontainer">
				<ul>
					<li><a href="#/items/computers">Computers</a></li>
					<li><a href="#/items/phones">Phones</a></li>
					<li><a href="#/items/printers">Printers</a></li>
					<li><a href="#/items/others">Others</a></li>
				</ul>
			</div>
		</div>
	</div>

	<div ng-view></div>
	

<script src="resources/js/controller/ItemListController.js"></script>
<script src="resources/js/controller/ItemDetailsController.js"></script>

</body>
</html>

This is the template for List page of specific item category:
item_computers.html

<div class="generic-container">
	<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 href="#/items/computerdetails/{{item.id}}">{{item.model}}</a></td>
	                    <td>{{item.price}}</td>
	                </tr>
	            </tbody>
			</table>
		</div>
	</div>
</div>

This is the template for Details page of specific item from specific item category:
item_computer_details.html

<div class="generic-container">
	<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>
                </tr>
                <tr>
                    <th>Brand</th><td>{{itemDetailsCtrl.item.brand}}</td>
                </tr>
                <tr>
                    <th>Model</th><td>{{itemDetailsCtrl.item.model}}</td>
                </tr>
                <tr>
                    <th>State</th><td>{{itemDetailsCtrl.item.state}}</td>
                </tr>
                <tr>
                    <th>Price (EUR)</th><td>{{itemDetailsCtrl.item.price}}</td>
                </tr>
                <tr>
                    <th>Processor (GHZ)</th><td>{{itemDetailsCtrl.item.processorSpeed}}</td>
                </tr>
                <tr>
                    <th>RAM (GB)</th><td>{{itemDetailsCtrl.item.ram}}</td>
                </tr>
                <tr>
                    <th>Disk Capacity</th><td>{{itemDetailsCtrl.item.diskCapacity}}</td>
				</tr>
			</table>
		</div>
	</div>
</div>

item_phones.html

<div class="generic-container">
	<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 href="#/items/phonedetails/{{item.id}}">{{item.model}}</a></td>
	                    <td>{{item.price}}</td>
	                </tr>
	            </tbody>
			</table>
		</div>
	</div>
</div>

item_phone_details.html

<div class="generic-container">
	<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>
                </tr>
                <tr>
                    <th>Brand</th><td>{{itemDetailsCtrl.item.brand}}</td>
                </tr>
                <tr>
                    <th>Model</th><td>{{itemDetailsCtrl.item.model}}</td>
                </tr>
                <tr>
                    <th>State</th><td>{{itemDetailsCtrl.item.state}}</td>
                </tr>
                <tr>
                    <th>Price (EUR)</th><td>{{itemDetailsCtrl.item.price}}</td>
                </tr>
                <tr>
                    <th>Size</th><td>{{itemDetailsCtrl.item.size}}</td>
                </tr>
                <tr>
                    <th>Camera (MB)</th><td>{{itemDetailsCtrl.item.camera}}</td>
                </tr>
                <tr>
                    <th>SD Card (GB)</th><td>{{itemDetailsCtrl.item.sdCard}}</td>
                </tr>
                <tr>
                    <th>Replaceable Battery</th><td>{{itemDetailsCtrl.item.replacableBattery}}</td>
				</tr>
			</table>
		</div>
	</div>
</div>

item_printers.html

<div class="generic-container">
	<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 href="#/items/printerdetails/{{item.id}}">{{item.model}}</a></td>
	                    <td>{{item.price}}</td>
	                </tr>
	            </tbody>
			</table>
		</div>
	</div>
</div>

item_printer_details.html

<div class="generic-container">
	<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>
                </tr>
                <tr>
                    <th>Brand</th><td>{{itemDetailsCtrl.item.brand}}</td>
                </tr>
                <tr>
                    <th>Model</th><td>{{itemDetailsCtrl.item.model}}</td>
                </tr>
                <tr>
                    <th>State</th><td>{{itemDetailsCtrl.item.state}}</td>
                </tr>
                <tr>
                    <th>Price (EUR)</th><td>{{itemDetailsCtrl.item.price}}</td>
                </tr>
                <tr>
                    <th>Technology</th><td>{{itemDetailsCtrl.item.technologie}}</td>
                </tr>
                <tr>
                    <th>Paper Format</th><td>{{itemDetailsCtrl.item.paperFormat}}</td>
                </tr>
                <tr>
                    <th>PPM</th><td>{{itemDetailsCtrl.item.pagesPerMinute}}</td>
				</tr>
			</table>
		</div>
	</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:

MainTemplateController.java

package com.websystique.springmvc.controller;

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

@Controller
@RequestMapping("/items")
public class MainTemplateController {
	
    @RequestMapping(value="/computers")
    public String getComputersTemplate() {
    	return "template/item_computers";	
    }

    @RequestMapping(value="/phones")
    public String getPhonesTemplate() {
    	return "template/item_phones";	
    }

    @RequestMapping(value="/printers")
    public String getPrintersTemplate() {
    	return "template/item_printers";	
    }

    @RequestMapping(value="/computerdetails")
    public String getComputerDetailsTemplate() {
    	return "template/item_computer_details";	
    }

    @RequestMapping(value="/phonedetails")
    public String getPhoneDetailsTemplate() {
    	return "template/item_phone_details";	
    }

    @RequestMapping(value="/printerdetails")
    public String getPrinterDetailsTemplate() {
    	return "template/item_printer_details";	
    }

}

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
@RequestMapping("/item")
public class ItemController {

    @Autowired
    ItemService itemService;  //Service which will do all data retrieval/manipulation work
 
	@RequestMapping(value="/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="/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="/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="/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="/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="/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/ItemServiceImpl.java

package com.websystique.springmvc.service;

import java.util.List;

public interface ItemService {
	List findItemsByCategory(String category);
	Object findItemById(long id, String category);
}

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;
	}
	
	
	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:

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 + "]";
	}

	
	
}
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 + "]";
	}

	
	
}
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
				+ "]";
	}
	
}

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 http://localhost:8080/Spring4MVCAngularJSRoutingExample/. it will be redirected to /item/computes [look at ‘otherwise’ part in $routeProvider config]. Check the URL in browser.

Spring4MVCAngularJSRoutingExample_img1

You can also open the developer tools in chrome and check the request sent for each operation to server and response received.

Now click on printers link, you should see all printers list in bottom panel.

Spring4MVCAngularJSRoutingExample_img3

Now click on specific printer link [in Model column], you should see the details of that specific printer in bottom panel.Also check the URL in browser.

Spring4MVCAngularJSRoutingExample_img4

That’s it.

Download Source Code



References

If you like tutorials on this site, why not take a step further and connect me on Facebook , Google Plus & Twitter as well? I would love to hear your thoughts on these articles, it will help me improve further our learning process.

If you appreciate the effort I have put in this learning site, help me improve the visibility of this site towards global audience by sharing and linking this site from within and beyond your network. You & your friends can always link my site from your site on www.websystique.com, and share the learning.

After all, we are here to learn together, aren’t we?

  • Hunters

    Hi websystique

    I see that you used 2 different URL: 1 for template, 1 for Rest Service.
    Can I make Spring always return 1 page?
    Example: When I open
    http://app/ -> return index.html

    http://app/items?id=id -> return index.html

    And them in the angular app, I can call Rest to http://app/items?id=id

    • websystique

      Hi, For that, you can override default handler mapping, mapping all your requests to one controller. Have a look at this link.

  • Yash Ganatra

    Your web.xml file?
    what url-pattern you gave to your dispatcher??

    • websystique

      It’s annotation based.For mapping, look at HelloWorldInitializer. It is “/”.

      • Yash Ganatra

        Ok but then tell me one thing / means it will applied to all files right then how your resources files like js and CSS are directly served by container?

        • Yash Ganatra

          Hopefully m clear enough to ask dis question! Actually I am new to spring so it can be silly .

  • Harshit C.

    Nice example but has too many problems after importing to eclipse.

    • EspressoK

      Harshit C., create the project from the command line using something like this
      mvn -B archetype:generate -DgroupId=com.websystique.springmvc -DartifactId=Spring4MVCAngularJSRoutingExample -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeVersion=1.0 -DinteractiveMode=false

      And then convert it to eclipse project using the command mvn eclipse:eclipse. Finally, import it in eclipse as an existing maven project. No errors :-), all the best!

      • EspressoK

        Remember to delete the index.jsp and web.xml file in your project structure

  • Gary Marshall
  • Pingback: Spring 4 MVC REST Service Example using @RestController - WebSystique()

  • Utkirbek Boltaev

    the other port is not working

    • websystique

      If you meant using another port, you can change in the URL provided inside $http calls in service.

      • Utkirbek Boltaev

        server: pivotal tc

        • Gary Marshall

          What I was trying to say was that when I used this in my html code: a href=”#/items…” (with the forward slash) it did not work. Click on a link defined that way did not run the angular code. What worked was a href=”#items (with no forward slash). Clicking on a link defined this way did run the angular code and everything worked fine.

          • websystique

            Hi Gary,
            I just tried the URL http://localhost:8080/Spring4MVCAngularJSRoutingExample/#/items/computers and it worked. If you want to remove # altogether from the URL itself, you can do so with $locationProvider in App.config :

            App.config([‘$routeProvider’, ‘$locationProvider’, function($routeProvider, $locationProvider) {
            …..
            $locationProvider.html5Mode(true);
            }

            and adding in section of index.html.

  • Alex M

    I have a little problem using your code. When I move the link http://localhost:8080/items/phones without ‘#’ observe HTML/Angular a code in the page.
    For example:
    http://localhost:8080/items/phones

    List of Phones

    ID Brand Model Price (EUR)

    {{item.id}} {{item.brand}} {{item.model}} {{item.price}}

    • websystique

      Hi Alex, I missed your message, sorry for that. Yes by defaut that # is necessary. But if you want to get rid of that, you can do so via following:

      App.config([‘$routeProvider’, ‘$locationProvider’, function($routeProvider, $locationProvider) {
      …..
      $locationProvider.html5Mode(true);
      ..
      }

      and adding in section of index.html.

  • CheY

    Great tutorial One question though … How do you make Angular Target the / in the App.js

    .when(‘/’, {
    templateUrl : ‘/’,
    });

    this brings up tomcat content …

    Thanks for your help

  • gaetan224

    sorry read next comment
    here is a screen shot:

    • websystique

      Hi Gaetan,

      Your ‘resources’ folder should be under webapp. Note that the ‘resources’ folder we are referring to is NOT the same as the one of Maven structure. This is configured in HelloWorldConfiguration

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

      In above configuration, resources is NOT the one from maven, but our own folder, inside webapp. You can even rename it to something else ['static' e.g.]

  • gaetan224

    Hello websystique i’m been following your tutorials they are really great, thanks very much,
    here is my problem,
    i’ve imported this tuto in “Eclipse Java EE IDE Kepler Release” as an existing project of eclipse, the index.html cannot get the css, js resources

    here is a screen shot:

  • Pingback: Spring 4 MVC ContentNegotiatingViewResolver example - WebSystique()

  • Pingback: Spring MVC @RequestBody @ResponseBody Example - WebSystique()

  • Максим Микрюков

    Good day, dear mentor. I beg your forgiveness for my English. First of all I want to express my gratitude for all that you do – you provide a very useful and relevant material.
    Now to the point:
    I have not changed anything in your project. Download -> unpack -> Run! Deployment produced on Tomcat. Since your settings DispatcherServlet mapping is listed as “/”, then I register in the address bar of my web browser appropriate route “localhost:8080/”. What am i doing wrong? Thank you in advance for your reply. I come from asp.net. What am I doing wrong? Thank you in advance for your reply.

    • websystique

      Hi Maxim,

      You must provide the context-path in URL. Every web application comes under a context path, you can’t simply ignore that fact.

      URL should be http://localhost:8080/Spring4MVCAngularJSRoutingExample/. Additionally I feel that your deployment on Tomcat is not done properly (as seen from screenshot).

      I would suggest you to visit
      Setting up tomcat with Eclipse to see if you have some setup issues in your local environment.

      Alternatively, you can simply download the code, open cmd-line, browse to this project and perform >mvn clean install, it will generate the war file, put this war into tomcat webapps directory, and click bin/startup.bat in tomcat directory. Please perform these steps and let me know the outcome.

      • Максим Микрюков

        Thank you for your advice! It really helped me

  • Максим Микрюков

    Example which downloaded from your site does not work!

    • websystique

      Hi Maxim,

      Good to see you again.

      I’ve just downloaded this example and successfully run BOTH from within Eclipse and also from external tomcat. There is no issue with this example.I have even run it on IE.

      Are you sure you have setup the project correctly? Did you make any change before running this example?

      Regarding your other question, this is an example of routing. Look at app.js which contains logic about how the partials will be loaded. Additionally, service contains the logic to intereract with Server while controller prepares the data to be shown in templates.

      If you have any doubts on the routing concept itself, please visit the AngularJS routing post to get help with the basics.

      About the screenshot you showed here, you won’t see the details on the right side(as you mentioned), they will be shown at the second half of the page ( as shown in my screenshots).

      I would suggest to re-download this example code and run it as it is, It will work. Let me know if you still face some issue, i am here to help you.

  • Максим Микрюков

    home page is loaded, but not one link for downloading the partial view is not working. This is not surprising, because the program is no reference to the partial representation of routing.

  • Pingback: AngularJS Routing Tutorial using ngRoute - WebSystique()