Creating a Simple Web App With Java 8, Spring Boot, and Angular
In this tutorial, we'll look at how developers can combine multiple technologies to make a web application. Read on to get started!
Join the DZone community and get the full member experience.
Join For FreePre-Requisites for Getting Started
Java 8 is installed.
Any Java IDE (preferably STS or IntelliJ IDEA).
Basic understanding of Java and Spring-based web development and UI development using HTML, CSS, and JavaScript.
Backdrop
In this article, I will try to create a small end-to-end web application using Java 8 and Spring Boot.
I have chosen SpringBoot because it is much easier to configure and plays well with other tech stacks. I have also used a REST API and SpringData JPA with an H2 database.
I used Spring Initializer to add all the dependencies and create a blank working project with all my configurations.
I have used Maven as the build tool, though Gradle can also be used.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>bootdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>bootDemo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath />
<!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
In the UI part, I have used AngularJS and BootStrap CSS along with basic JS, CSS, and HTML.
I tried to follow the coding guidelines as much as I could, but all suggestions are welcome.
This is a very simple project which can be useful for creating an end-to-end web application.
Package Structure
Implementation
Let's start with the SpringBootApplication class.
@SpringBootApplication
public class BootDemoApplication {
@Autowired
UserRepository userRepository;
public static void main(String[] args) {
SpringApplication.run(BootDemoApplication.class, args);
}
}
Let's create Controllers now.
@Controller
public class HomeController {
@RequestMapping("/home")
public String home() {
return "index";
}
}
This will act as the homepage for our SPA. Now we create a Controller to handle some REST calls.
@RequestMapping("/user")
@RestController
public class UserController {
@Autowired
UserService userService;
@RequestMapping(Constants.GET_USER_BY_ID)
public UserDto getUserById(@PathVariable Integer userId) {
return userService.getUserById(userId);
}
@RequestMapping(Constants.GET_ALL_USERS)
public List < UserDto > getAllUsers() {
return userService.getAllUsers();
}
@RequestMapping(value = Constants.SAVE_USER, method = RequestMethod.POST)
public void saveUser(@RequestBody UserDto userDto) {
userService.saveUser(userDto);
}
}
Here we have different methods to handle different test calls from the client side.
I have Autowired a Service class, UserService, in the Controller.
public interface UserService {
UserDto getUserById(Integer userId);
void saveUser(UserDto userDto);
List < UserDto > getAllUsers();
}
@Service
public class UserServiceimpl implements UserService {
@Autowired
UserRepository userRepository;
@Override
public UserDto getUserById(Integer userId) {
return UserConverter.entityToDto(userRepository.getOne(userId));
}
@Override
public void saveUser(UserDto userDto) {
userRepository.save(UserConverter.dtoToEntity(userDto));
}
@Override
public List < UserDto > getAllUsers() {
return userRepository.findAll().stream().map(UserConverter::entityToDto).collect(Collectors.toList());
}
}
In a typical web application, there are generally two types of data objects: DTO (to communicate through the client) and Entity (to communicate through the DB).
DTO
public class UserDto {
Integer userId;
String userName;
List<SkillDto> skillDtos= new ArrayList<>();
public UserDto(Integer userId, String userName, List<SkillDto> skillDtos) {
this.userId = userId;
this.userName = userName;
this.skillDtos = skillDtos;
}
public UserDto() {
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public List<SkillDto> getSkillDtos() {
return skillDtos;
}
public void setSkillDtos(List<SkillDto> skillDtos) {
this.skillDtos = skillDtos;
}
}
public class SkillDto {
Integer skillId;
String SkillName;
public SkillDto(Integer skillId, String skillName) {
this.skillId = skillId;
SkillName = skillName;
}
public SkillDto() {
}
public Integer getSkillId() {
return skillId;
}
public void setSkillId(Integer skillId) {
this.skillId = skillId;
}
public String getSkillName() {
return SkillName;
}
public void setSkillName(String skillName) {
SkillName = skillName;
}
}
Entity
@Entity
public class User implements Serializable{
private static final long serialVersionUID = 0x62A6DA99AABDA8A8L;
@Column
@GeneratedValue(strategy = GenerationType.AUTO)
@Id
private Integer userId;
@Column
private String userName;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Skill> skills= new LinkedList<>();
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public List<Skill> getSkills() {
return skills;
}
public void setSkills(List<Skill> skills) {
this.skills = skills;
}
public User() {
}
public User(String userName, List<Skill> skills) {
this.userName = userName;
this.skills = skills;
}
}
@Entity
public class Skill {
@Column
@GeneratedValue(strategy = GenerationType.AUTO)
@Id
private Integer skillId;
@Column
private String skillName;
@ManyToOne
private User user;
public Skill(String skillName) {
this.skillName = skillName;
}
public Integer getSkillId() {
return skillId;
}
public void setSkillId(Integer skillId) {
this.skillId = skillId;
}
public String getSkillName() {
return skillName;
}
public void setSkillName(String skillName) {
this.skillName = skillName;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Skill() {
}
public Skill(String skillName, User user) {
this.skillName = skillName;
this.user = user;
}
}
For DB operations, we use SpringData JPA:
@Repository
public interface UserRepository extends JpaRepository<User, Integer>{
}
@Repository
public interface SkillRepository extends JpaRepository<Skill, Integer>{
}
Extending JpaRepository
provides a lot of CRUD operations by default, and one can use it to create their own query methods as well. Please read more about this here.
To convert DTO -> Entity and Entity -> DTO, I created some basic converter classes.
public class UserConverter {
public static User dtoToEntity(UserDto userDto) {
User user = new User(userDto.getUserName(), null);
user.setUserId(userDto.getUserId());
user.setSkills(userDto.getSkillDtos().stream().map(SkillConverter::dtoToEntity).collect(Collectors.toList()));
return user;
}
public static UserDto entityToDto(User user) {
UserDto userDto = new UserDto(user.getUserId(), user.getUserName(), null);
userDto.setSkillDtos(user.getSkills().stream().map(SkillConverter::entityToDto).collect(Collectors.toList()));
return userDto;
}
}
public class SkillConverter {
public static Skill dtoToEntity(SkillDto SkillDto) {
Skill Skill = new Skill(SkillDto.getSkillName(), null);
Skill.setSkillId(SkillDto.getSkillId());
return Skill;
}
public static SkillDto entityToDto(Skill skill) {
return new SkillDto(skill.getSkillId(), skill.getSkillName());
}
}
Let's focus on the UI part now.
While using Angular, there are certain guidelines we need to follow.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Main Page</title>
</head>
<body ng-app="demo">
<hr/>
<div class="container" ng-controller="UserController">
<div class="row">
<label>User</label> <input type="text" ng-model="userDto.userName" class="input-sm spacing"/>
<label>Skills</label> <input type="text" ng-model="skills" ng-list class="input-sm custom-width spacing"
placeholder="use comma to separate skills"/>
<button ng-click="saveUser()" class="btn btn-sm btn-info">Save User</button>
</div>
<hr/>
<div class="row">
<p>{{allUsers | json}}</p>
</div>
<hr/>
<div class="row" ng-repeat="user in allUsers">
<div class="">
<h3>{{user.userName}}</h3>
<span ng-repeat="skill in user.skillDtos" class="spacing">{{skill.skillName}}</span>
</div>
</div>
</div>
</body>
<script src="js/lib/angular.min.js"></script>
<script src="js/lib/ui-bootstrap-tpls-2.5.0.min.js"></script>
<script src="js/app/app.js"></script>
<script src="js/app/UserController.js"></script>
<script src="js/app/UserService.js"></script>
<link rel="stylesheet" href="css/lib/bootstrap.min.css"/>
<link rel="stylesheet" href="css/app/app.css"/>
</html>
While creating the HTML, don't forget to import the required JS and CSS files.
app.js
'use strict'
var demoApp = angular.module('demo', ['ui.bootstrap', 'demo.controllers',
'demo.services'
]);
demoApp.constant("CONSTANTS", {
getUserByIdUrl: "/user/getUser/",
getAllUsers: "/user/getAllUsers",
saveUser: "/user/saveUser"
});
UserController.js
'use strict'
var module = angular.module('demo.controllers', []);
module.controller("UserController", ["$scope", "UserService",
function($scope, UserService) {
$scope.userDto = {
userId: null,
userName: null,
skillDtos: []
};
$scope.skills = [];
UserService.getUserById(1).then(function(value) {
console.log(value.data);
}, function(reason) {
console.log("error occured");
}, function(value) {
console.log("no callback");
});
$scope.saveUser = function() {
$scope.userDto.skillDtos = $scope.skills.map(skill => {
return {
skillId: null,
skillName: skill
};
});
UserService.saveUser($scope.userDto).then(function() {
console.log("works");
UserService.getAllUsers().then(function(value) {
$scope.allUsers = value.data;
}, function(reason) {
console.log("error occured");
}, function(value) {
console.log("no callback");
});
$scope.skills = [];
$scope.userDto = {
userId: null,
userName: null,
skillDtos: []
};
}, function(reason) {
console.log("error occured");
}, function(value) {
console.log("no callback");
});
}
}
]);
UserService.js
'use strict'
angular.module('demo.services', []).factory('UserService', ["$http", "CONSTANTS", function($http, CONSTANTS) {
var service = {};
service.getUserById = function(userId) {
var url = CONSTANTS.getUserByIdUrl + userId;
return $http.get(url);
}
service.getAllUsers = function() {
return $http.get(CONSTANTS.getAllUsers);
}
service.saveUser = function(userDto) {
return $http.post(CONSTANTS.saveUser, userDto);
}
return service;
}]);
app.css
body{
background-color: #efefef;
}
span.spacing{
margin-right: 10px;
}
input.custom-width{
width: 200px;
}
input.spacing{
margin-right: 5px;
}
The application can be built using:
mvn clean install
then run as a runnable jar file by using
java -jar bootdemo-0.0.1-SNAPSHOT.jar
or running the main file directly.
Open the browser and hit http://localhost:8080/home
One simple page will open. Enter the name and skills and the entered data will be persisted in the DB.
In the application.properties files, two configurations are to be added.
spring.mvc.view.prefix = /views/
spring.mvc.view.suffix = .html
The source code can be cloned or downloaded from here.
If you enjoyed this article and want to learn more about Angular, check out our compendium of tutorials and articles from JS to 8.
Published at DZone with permission of Ashish Lohia, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments