1. Gambaran keseluruhan
Spring Boot and Angular membentuk tandem kuat yang berfungsi hebat untuk mengembangkan aplikasi web dengan jejak minimum.
Dalam tutorial ini, kami akan menggunakan Spring Boot untuk menerapkan backend RESTful, dan Angular untuk membuat frontend berasaskan JavaScript.
2. Aplikasi Spring Boot
Fungsi aplikasi web demo kami akan cukup sederhana. Ini hanya akan disempitkan untuk mengambil dan menampilkan Senarai entiti JPA dari pangkalan data H2 dalam memori, dan yang masih baru melalui bentuk HTML biasa.
2.1. Ketergantungan Maven
Berikut adalah pergantungan projek Spring Boot kami:
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-jpa com.h2database h2 runtime
Perhatikan bahawa kami menyertakan spring-boot-starter-web kerana kami akan menggunakannya untuk membuat perkhidmatan REST, dan spring-boot-starter-jpa untuk menerapkan lapisan kegigihan.
Versi pangkalan data H2 juga dikendalikan oleh ibu bapa Spring Boot.
2.2. Kelas Entiti JPA
Untuk membuat prototaip lapisan domain aplikasi kami dengan cepat, mari kita tentukan kelas entiti JPA yang mudah, yang akan bertanggungjawab untuk memodelkan pengguna:
@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; private final String name; private final String email; // standard constructors / setters / getters / toString }
2.3. The UserRepository Interface
Oleh kerana kita memerlukan fungsi CRUD asas pada entiti Pengguna , kita juga mesti menentukan antara muka UserRepository :
@Repository public interface UserRepository extends CrudRepository{}
2.4. Pengawal REST
Sekarang, mari kita laksanakan REST API. Dalam kes ini, ini hanya pengawal REST yang mudah.
@RestController @CrossOrigin(origins = "//localhost:4200") public class UserController { // standard constructors private final UserRepository userRepository; @GetMapping("/users") public List getUsers() { return (List) userRepository.findAll(); } @PostMapping("/users") void addUser(@RequestBody User user) { userRepository.save(user); } }
Tidak ada sesuatu yang kompleks dalam definisi kelas UserController .
Sudah tentu, satu-satunya perincian pelaksanaan yang perlu diperhatikan di sini ialah penggunaan anotasi @CrossOrigin . Seperti namanya, anotasi membolehkan Cross-Origin Resource Sharing (CORS) pada pelayan.
Langkah ini tidak selalu diperlukan. Oleh kerana kami menggunakan frontend sudut ke // localhost: 4200 dan backend Boot kami ke // localhost: 8080 , penyemak imbas akan menolak permintaan dari satu ke yang lain.
Mengenai kaedah pengawal, getUser () mengambil semua entiti Pengguna dari pangkalan data. Begitu juga, kaedah addUser () tetap ada entiti baru dalam pangkalan data, yang diteruskan dalam badan permintaan.
Untuk memastikan perkara-perkara mudah, kami sengaja meninggalkan pelaksanaan pengendali yang mencetuskan pengesahan Spring Boot sebelum meneruskan entiti. Dalam pengeluaran, bagaimanapun, kami tidak dapat mempercayai input pengguna, jadi pengesahan dari sisi server harus menjadi ciri wajib.
2.5. Bootstrap Aplikasi Boot Musim Semi
Akhirnya, mari buat kelas bootstrap Spring Boot standard dan isi pangkalan data dengan beberapa entiti Pengguna :
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @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); }; } }
Sekarang, mari jalankan aplikasinya. Seperti yang dijangkakan, kita akan melihat senarai entiti Pengguna yang dicetak ke konsol semasa memulakan:
User{id=1, name=John, [email protected]} User{id=2, name=Julie, [email protected]} User{id=3, name=Jennifer, [email protected]} User{id=4, name=Helen, [email protected]} User{id=5, name=Rachel, [email protected]}
3. Aplikasi Sudut
Dengan aplikasi demo Spring Boot yang aktif dan aktif, mari kita buat aplikasi Angular sederhana, yang mampu menggunakan API pengawal REST.
3.1. Pemasangan CLI sudut
Kami akan menggunakan Angular CLI, utiliti baris perintah yang kuat, untuk membuat aplikasi Angular kami.
Angular CLI adalah alat yang sangat berharga kerana ia membolehkan kita membuat keseluruhan projek Angular dari awal, menghasilkan komponen, perkhidmatan, kelas, dan antara muka dengan hanya beberapa arahan .
Setelah kami memasang npm (Node Package Manager), kami akan membuka konsol arahan dan menaip perintah:
npm install -g @angular/[email protected]
Itu sahaja. Perintah di atas akan memasang versi terbaru Angular CLI.
3.2. Perancah Projek dengan CLI Sudut
Sebenarnya, kita dapat menghasilkan struktur aplikasi Sudut kita dari bawah ke atas. Tetapi secara jujur, ini adalah tugas yang ralat dan memakan masa yang harus kita hindari dalam semua keadaan.
Sebaliknya, kami akan membiarkan CLI Angular melakukan kerja keras untuk kami. Oleh itu, mari buka konsol arahan, kemudian arahkan ke folder di mana kami mahu aplikasi kami dibuat dan taipkan arahan:
ng new angularclient
Yang baru arahan akan menjana struktur permohonan keseluruhan dalam angularclient direktori.
3.3. Titik Masuk Aplikasi Sudut
Sekiranya kita melihat ke dalam folder angularclient , kita akan melihat bahawa Angular CLI secara berkesan membuat keseluruhan projek untuk kita.
Angular's application files use TypeScript, a typed superset of JavaScript that compiles to plain JavaScript. However, the entry point of any Angular application is a plain old index.html file.
Let's edit this file, as follows:
Spring Boot - Angular Application
As we can see above, we included Bootstrap 4, so we can give our application UI components a more fancy look. Of course, it's possible to pick up another UI kit from the bunch available out there.
Please notice the custom tags inside the section. At first sight, they look rather weird, as is not a standard HTML 5 element.
Let's keep them right there, as is the root selector that Angular uses for rendering the application's root component.
3.4. The app.component.ts Root Component
To better understand how Angular binds an HTML template to a component, let's go to the src/app directory and edit the app.component.ts TypeScript file – the root component:
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title: string; constructor() { this.title = 'Spring Boot - Angular Application'; } }
For obvious reasons, we'll not dive deep into learning TypeScript. Even so, let's notice 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:
- selector – the HTML selector used to bind the component to the HTML template file
- templateUrl – the HTML template file associated with the component
- styleUrls – one or more CSS files associated with the component
As expected, we can use the app.component.html and app.component.css files to define the HTML template and the CSS styles of the root component.
Finally, the selector element binds the whole component to the selector included in the index.html file.
3.5. The app.component.html File
Since the app.component.html file allows us to define the root component's HTML template — the AppComponent class — we'll use it for creating a basic navigation bar with two buttons.
If we click the first button, Angular will display a table containing the list of User entities stored in the database. Similarly, if we click the second one, it will render an HTML form, which we can use for adding new entities to the database:
{{ title }}
- List Users
- Add User
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 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:
ng generate class user
Angular CLI will generate an empty User class. Let's populate it with a few fields:
export class User { id: string; name: string; email: string; }
3.7. The UserService Service
With our client-side domain User class already set, let's now implement a service class that performs GET and POST requests to the //localhost:8080/users endpoint.
This will allow us to encapsulate access to the REST controller in a single class, which we can reuse throughout the entire application.
Let's open a console terminal, then create a service directory, and within that directory, issue the following command:
ng generate service user-service
Now, let's open the user.service.ts file that Angular CLI just created and refactor it:
import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { User } from '../model/user'; import { Observable } from 'rxjs/Observable'; @Injectable() export class UserService { private usersUrl: string; constructor(private http: HttpClient) { this.usersUrl = '//localhost:8080/users'; } public findAll(): Observable { return this.http.get(this.usersUrl); } public save(user: User) { return this.http.post(this.usersUrl, user); } }
We don't need a solid background on TypeScript to understand how the UserService class works. Simply put, it encapsulates within a reusable component all the functionality required to consume the REST controller API that we implemented before in Spring Boot.
The findAll() method performs a GET HTTP request to the //localhost:8080/users endpoint via Angular's HttpClient. The method returns an Observable instance that holds an array of User objects.
Likewise, the save() method performs a POST HTTP request to the //localhost:8080/users endpoint.
By specifying the type User in the HttpClient‘s request methods, we can consume back-end responses in an easier and more effective way.
Lastly, let's notice the use of the @Injectable() metadata marker. This signals that the service should be created and injected via Angular's dependency injectors.
3.8. The UserListComponent Component
In this case, the UserService class is the thin middle-tier between the REST service and the application's presentation layer. Therefore, we need to define a component responsible for rendering the list of User entities persisted in the database.
Let's open a terminal console, then create a user-list directory and generate a user list component:
ng generate component user-list
Angular CLI will generate an empty component class that implements the ngOnInit interface. The interface declares a hook ngOnInit() method, which Angular calls after it has finished instantiating the implementing class, and after calling its constructor, too.
Let's refactor the class so that it can take a UserService instance in the constructor:
import { Component, OnInit } from '@angular/core'; import { User } from '../model/user'; import { UserService } from '../service/user.service'; @Component({ selector: 'app-user-list', templateUrl: './user-list.component.html', styleUrls: ['./user-list.component.css'] }) export class UserListComponent implements OnInit { users: User[]; constructor(private userService: UserService) { } ngOnInit() { this.userService.findAll().subscribe(data => { this.users = data; }); } }
The implementation of the UserListComponent class is pretty self-explanatory. It simply uses the UserService's findAll() method to fetch all the entities persisted in the database and stores them in the users field.
Additionally, we need to edit the component's HTML file, user-list.component.html, to create the table that displays the list of entities:
#
Name
Email
{{ user.id }}
{{ user.name }}
{{ user.email }}
Notice 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.
3.9. The UserFormComponent Component
Similarly, we need to create a component that allows us to persist a new User object in the database.
Let's create a user-form directory and type the following:
ng generate component user-form
Next, let's open the user-form.component.ts file and add to the UserFormComponent class a method for saving a User object:
import { Component } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { UserService } from '../service/user.service'; import { User } from '../model/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']); } }
In this case, UserFormComponent also takes a UserService instance in the constructor, which the onSubmit() method uses for saving the supplied User object.
Since we need to redisplay the updated list of entities once we have persisted a new one, we call the gotoUserList() method after the insertion, which redirects the user to the /users path.
In addition, we need to edit the user-form.component.html file and create the HTML form for persisting a new user in the database:
Name Name is required Email Email is required Submit
At a glance, the form looks pretty standard. But it encapsulates a lot of Angular's functionality behind the scenes.
Let's notice the use of the ngSubmit directive, which calls the onSubmit() method when the form is submitted.
Next, we have defined the template variable #userForm, so Angular adds automatically an NgForm directive, which allows us to keep track of the form as a whole.
The NgForm directive holds the controls that we created for the form elements with an ngModel directive and a name attribute and also monitors their properties, including their state.
The ngModel directive gives us two-way data binding functionality between the form controls and the client-side domain model – the User class.
This means that data entered in the form input fields will flow to the model – and the other way around. Changes in both elements will be reflected immediately via DOM manipulation.
Additionally, ngModel allows us to keep track of the state of each form control and perform client-side validation, by adding to each control different CSS classes and DOM properties.
In the above HTML file, we used the properties applied to the form controls only to display an alert box when the values in the form have been changed.
3.10. The app-routing.module.ts File
Although the components are functional in isolation, we still need to use a mechanism for calling them when the user clicks the buttons in the navigation bar.
This is where the RouterModule comes into play. So, let's open the app-routing.module.ts file, and configure the module, so it can dispatch requests to the matching components:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { UserListComponent } from './user-list/user-list.component'; import { UserFormComponent } from './user-form/user-form.component'; const routes: Routes = [ { path: 'users', component: UserListComponent }, { path: 'adduser', component: UserFormComponent } ]; @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:
- Path – a string that matches the URL in the browser address bar
- 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 placeholder.
Likewise, if they click the Add User button, it will render the UserFormComponent component.
3.11. The app.module.ts File
Next, we need to edit the app.module.ts file, so Angular can import all the required modules, components, and services.
Additionally, we need to specify which provider we'll use for creating and injecting the UserService class. Otherwise, Angular won't be able to inject it into the component classes:
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { FormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; import { UserListComponent } from './user-list/user-list.component'; import { UserFormComponent } from './user-form/user-form.component'; import { UserService } from './service/user.service'; @NgModule({ declarations: [ AppComponent, UserListComponent, UserFormComponent ], imports: [ BrowserModule, AppRoutingModule, HttpClientModule, FormsModule ], providers: [UserService], bootstrap: [AppComponent] }) export class AppModule { }
4. Running the Application
Finally, we're ready to run our application.
To accomplish this, let's first run the Spring Boot application, so the REST service is alive and listening for requests.
Setelah aplikasi Spring Boot dimulakan, mari buka konsol arahan dan taipkan arahan berikut:
ng serve --open
Ini akan memulakan pelayan pengembangan langsung Angular dan juga membuka penyemak imbas di // localhost: 4200 .
Kita harus melihat bar navigasi dengan butang untuk menyenaraikan entiti yang ada dan untuk menambah entiti baru. Sekiranya kita mengklik butang pertama, kita akan melihat di bawah bar navigasi jadual dengan senarai entiti yang terdapat dalam pangkalan data:
Begitu juga, mengklik butang kedua akan memaparkan borang HTML untuk mengekalkan entiti baru:

5. Kesimpulan
Dalam tutorial ini, kami belajar bagaimana membina aplikasi web asas dengan Spring Boot and Angular .
Seperti biasa, semua contoh kod yang ditunjukkan dalam tutorial ini terdapat di GitHub.