Spring Boot Application With Angular
Spring Boot Application With Angular
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private final String name;
private final String email;
// standard constructors / setters / getters / toString
}
@RestController
@CrossOrigin(origins = "http://localhost:4200")
public class UserController {
// standard constructors
@GetMapping("/users")
public List<User> getUsers() {
return (List<User>) userRepository.findAll();
}
@PostMapping("/users")
void addUser(@RequestBody User user) {
userRepository.save(user);
}
}
There's nothing inherently complex in the definition of
the UserController class.
Of course, the implementation detail worth noting here is the use of
the @CrossOrigin annotation. As the name implies, the annotation
enables Cross-Origin Resource Sharing (CORS) on the server.
This step isn't always necessary, but since we're deploying our Angular
frontend to http://localhost:4200, and our Boot backend
to http://localhost:8080, the browser would otherwise deny requests
from one to the other.
Regarding the controller methods, getUser() fetches all the User entities
from the database. Similarly, the addUser() method persists a new entity
in the database, which is passed in the request body.
To keep things simple, we deliberately left out the controller
implementation triggering Spring Boot validation before persisting an
entity. In production, however, we can't trust user input alone, so server-
side validation should be a mandatory feature.
@SpringBootApplication
public class Application {
@Bean
CommandLineRunner init(UserRepository userRepository) {
return args -> {
Stream.of("John", "Julie", "Jennifer", "Helen",
"Rachel").forEach(name -> {
User user = new User(name, name.toLowerCase() +
"@domain.com");
userRepository.save(user);
});
userRepository.findAll().forEach(System.out::println);
};
}
}
That's it. The above command will install the latest version of Angular
CLI.
The new command will generate the entire application structure within
the angularclient directory.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Spring Boot - Angular Application</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min
.css"
integrity="sha384-
Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous">
</head>
<body>
<app-root></app-root>
</body>
</html>
title: string;
constructor() {
this.title = 'Spring Boot - Angular Application';
}
}
For obvious reasons, we won't dive deep into learning TypeScript. Even
so, let's note that the file defines an AppComponent class, which
declares a field title of type string (lower-cased). Definitively, it's typed
JavaScript.
Additionally, the constructor initializes the field with a string value, which
is pretty similar to what we do in Java.
The most relevant part is the @Component metadata marker or
decorator, which defines three elements:
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="card bg-dark my-5">
<div class="card-body">
<h2 class="card-title text-center text-white py-3">{{ title
}}</h2>
<ul class="text-center list-inline py-3">
<li class="list-inline-item">
<a routerLink="/users" class="btn btn-info">List
Users</a>
</li>
<li class="list-inline-item">
<a routerLink="/adduser" class="btn btn-info">Add
User</a>
</li>
</ul>
</div>
</div>
<router-outlet></router-outlet>
</div>
</div>
</div>
The bulk of the file is standard HTML, with a few caveats worth noting.
The first one is the {{ title }} expression. The double curly braces {{
variable-name }} is the placeholder that Angular uses for performing
variable interpolation.
Let's keep in mind that the AppComponent class initialized the title field
with the value Spring Boot – Angular Application. Thus, Angular will
display the value of this field in the template. Likewise, changing the
value in the constructor will be reflected in the template.
The second thing to note is the routerLink attribute.
Angular uses this attribute for routing requests through its routing
module (more on this later). For now, it's sufficient to know that the
module will dispatch a request to the /users path to a specific
component and a request to /adduser to another component.
In each case, the HTML template associated with the matching
component will be rendered within the <router-outlet></router-
outlet> placeholder.
3.6. The User Class
Since our Angular application will fetch from and persist User entities in
the database, let's implement a simple domain model with TypeScript.
Let's open a terminal console and create a model directory:
Angular CLI will generate an empty User class, so let's populate it with a
few fields:
Now let's open the user.service.ts file that Angular CLI just created and
refactor it:
@Injectable()
export class UserService {
private usersUrl: string;
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.css']
})
export class UserListComponent implements OnInit {
users: User[];
ngOnInit() {
this.userService.findAll().subscribe(data => {
this.users = data;
});
}
}
We should note the use of the *ngFor directive. The directive is called
a repeater, and we can use it for iterating over the contents of a variable
and iteratively rendering HTML elements. In this case, we used it
for dynamically rendering the table's rows.
In addition, we used variable interpolation for showing the id, name,
and email of each user.
@Component({
selector: 'app-user-form',
templateUrl: './user-form.component.html',
styleUrls: ['./user-form.component.css']
})
export class UserFormComponent {
user: User;
constructor(
private route: ActivatedRoute,
private router: Router,
private userService: UserService) {
this.user = new User();
}
onSubmit() {
this.userService.save(this.user).subscribe(result =>
this.gotoUserList());
}
gotoUserList() {
this.router.navigate(['/users']);
}
}
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
As we can see above, the Routes array instructs the router which
component to display when a user clicks a link or specifies a URL into
the browser address bar.
A route is composed of two parts:
1. Path – a string that matches the URL in the browser address bar
2. Component – the component to create when the route is active
(navigated)
If the user clicks the List Users button, which links to the /users path, or
enters the URL in the browser address bar, the router will render
the UserListComponent component's template file in the <router-
outlet> placeholder.
Likewise, if they click the Add User button, it will render
the UserFormComponent component.
@NgModule({
declarations: [
AppComponent,
UserListComponent,
UserFormComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FormsModule
],
providers: [UserService],
bootstrap: [AppComponent]
})
export class AppModule { }
ng serve –open
This will start Angular's live development server and also open the
browser at http://localhost:4200.
We should see the navigation bar with the buttons for listing existing
entities and for adding new ones. If we click the first button, we should
see below the navigation bar a table with the list of entities persisted in
the database:
Similarly, clicking the second button will display the HTML form for
persisting a new entity: