SpringBootとAngularを使用したWebアプリケーションの構築
1. 概要
SpringBootとAngularは強力なタンデムを形成し、最小限のフットプリントでWebアプリケーションを開発するのに最適です。
このチュートリアルでは、 Spring Bootを使用してRESTfulバックエンドを実装し、Angularを使用してJavaScriptベースのフロントエンドを作成します。
2. SpringBootアプリケーション
私たちのデモWebアプリケーションの機能は確かにかなり単純化されます。 インメモリH2データベースからJPAエンティティのListをフェッチして表示し、プレーンなHTMLフォームを介して新しいエンティティを永続化するように絞り込まれます。
2.1. Mavenの依存関係
Spring Bootプロジェクトの依存関係は次のとおりです。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
spring-boot-starter-web をRESTサービスの作成に使用するため、spring-boot-starter-jpaを永続層の実装に使用することに注意してください。
H2データベースバージョンも、SpringBootの親によって管理されます。
2.2. JPAエンティティクラス
アプリケーションのドメインレイヤーのプロトタイプをすばやく作成するために、ユーザーのモデリングを担当する単純なJPAエンティティークラスを定義しましょう。
@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. UserRepositoryインターフェース
User エンティティで基本的なCRUD機能が必要になるため、UserRepositoryインターフェイスも定義する必要があります。
@Repository
public interface UserRepository extends CrudRepository<User, Long>{}
2.4. RESTコントローラー
それでは、RESTAPIを実装しましょう。 この場合、それは単なるRESTコントローラーです。
@RestController
@CrossOrigin(origins = "http://localhost:4200")
public class UserController {
// standard constructors
private final UserRepository userRepository;
@GetMapping("/users")
public List<User> getUsers() {
return (List<User>) userRepository.findAll();
}
@PostMapping("/users")
void addUser(@RequestBody User user) {
userRepository.save(user);
}
}
UserControllerクラスの定義には本質的に複雑なものはありません。
もちろん、ここで注目に値する実装の詳細は、@CrossOriginアノテーションの使用です。 名前が示すように、アノテーションはサーバー上でクロスオリジンリソースシェアリング(CORS)を有効にします。
この手順は必ずしも必要ではありませんが、 Angularフロントエンドをhttp:// localhost:4200 にデプロイし、ブートバックエンドを http://にデプロイしているためです。 localhost:8080 、それ以外の場合、ブラウザーは一方から他方への要求を拒否します。
コントローラメソッドに関しては、 getUser()はデータベースからすべてのUserエンティティをフェッチします。 同様に、 addUser()メソッドは、リクエストボディで渡される新しいエンティティをデータベースに保持します。
簡単にするために、エンティティを永続化する前に、SpringBoot検証をトリガーするコントローラー実装を意図的に省略しました。 ただし、本番環境では、ユーザー入力だけを信頼することはできないため、サーバー側の検証は必須の機能である必要があります。
2.5. SpringBootアプリケーションのブートストラップ
最後に、標準のSpring Bootブートストラップクラスを作成し、データベースにいくつかのUserエンティティを入力します。
@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);
};
}
}
それでは、アプリケーションを実行してみましょう。 予想どおり、起動時にUserエンティティのリストがコンソールに出力されます。
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. Angularアプリケーション
デモのSpringBootアプリケーションを起動して実行すると、RESTコントローラーAPIを使用できる単純なAngularアプリケーションを作成できるようになります。
3.1. AngularCLIのインストール
強力なコマンドラインユーティリティであるAngularCLI を使用して、Angularアプリケーションを作成します。
Angular CLIは、 Angularプロジェクト全体を最初から作成し、いくつかのコマンドでコンポーネント、サービス、クラス、およびインターフェイスを生成できるため、非常に価値のあるツールです。
npm (ノードパッケージマネージャー)をインストールしたら、コマンドコンソールを開き、次のコマンドを入力します。
npm install -g @angular/[email protected]
それでおしまい。 上記のコマンドは、最新バージョンのAngularCLIをインストールします。
3.2. AngularCLIを使用したプロジェクトの足場
Angularアプリケーション構造をゼロから生成することはできますが、正直なところ、これはエラーが発生しやすく時間のかかるタスクであり、すべての場合に回避する必要があります。
代わりに、AngularCLIにハードワークを任せます。 したがって、コマンドコンソールを開き、アプリケーションを作成するフォルダーに移動して、次のコマンドを入力します。
ng new angularclient
new コマンドは、angleclientディレクトリ内にアプリケーション構造全体を生成します。
3.3. Angularアプリケーションのエントリポイント
angleclient フォルダーの内部を見ると、AngularCLIがプロジェクト全体を効果的に作成していることがわかります。
Angularのアプリケーションファイルは、プレーンJavaScriptにコンパイルされるJavaScriptの型付きスーパーセットであるTypeScriptを使用します。ただし、Angularアプリケーションのエントリポイントは、プレーンな古いindex.htmlファイルです。
このファイルを編集しましょう:
<!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>
上記のように、 Bootstrap 4 を含めたので、アプリケーションUIコンポーネントをより洗練された外観にすることができます。 もちろん、そこにあるたくさんのUIキットから別のUIキットを入手することも可能です。
習慣に注意してください
として、それらをそこに保持します
3.4. app.component.tsルートコンポーネント
AngularがHTMLテンプレートをコンポーネントにバインドする方法をよりよく理解するために、 src / app ディレクトリに移動し、ルートコンポーネントである app.component.tsTypeScriptファイルを編集してみましょう。
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';
}
}
明らかな理由から、TypeScriptの学習については深く掘り下げません。 それでも、ファイルが AppComponent クラスを定義していることに注意してください。このクラスは、タイプ string (小文字)のフィールドtitleを宣言します。 間違いなく、JavaScriptと入力されています。
さらに、コンストラクターはフィールドを string 値で初期化します。これは、Javaで行うことと非常によく似ています。
最も関連性の高い部分は、@Componentメタデータマーカーまたはデコレーターです。これは次の3つの要素を定義します。
- セレクター–コンポーネントをHTMLテンプレートファイルにバインドするために使用されるHTMLセレクター
- templateUrl –コンポーネントに関連付けられたHTMLテンプレートファイル
- styleUrls –コンポーネントに関連付けられた1つ以上のCSSファイル
予想どおり、app.component.htmlファイルとapp.component.cssファイルを使用して、ルートコンポーネントのHTMLテンプレートとCSSスタイルを定義できます。
最後に、 セレクタ要素は、コンポーネント全体をにバインドします
3.5. app.component.htmlファイル
app.component.html ファイルを使用すると、ルートコンポーネントのHTMLテンプレートである AppComponent クラスを定義できるため、基本的なナビゲーションの作成に使用します。 2つのボタンが付いたバー。
最初のボタンをクリックすると、Angularはデータベースに保存されているUserエンティティのリストを含むテーブルを表示します。 同様に、2番目のフォームをクリックすると、データベースに新しいエンティティを追加するために使用できるHTMLフォームがレンダリングされます。
<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>
ファイルの大部分は標準のHTMLですが、注意すべき点がいくつかあります。
AppComponentクラスがtitleフィールドを値Spring Boot– AngularApplicationで初期化したことに注意してください。 したがって、Angularはこのフィールドの値をテンプレートに表示します。 同様に、コンストラクターで値を変更すると、テンプレートに反映されます。
2番目に注意すべきことはrouterLink属性です。
Angularは、ルーティングモジュールを介してリクエストをルーティングするためにこの属性を使用します(これについては後で詳しく説明します)。 今のところ、モジュールが特定のコンポーネントへの / users パスへの要求と、別のコンポーネントへの /adduserへの要求をディスパッチすることを知っていれば十分です。
いずれの場合も、一致するコンポーネントに関連付けられたHTMLテンプレートは、
3.6. ユーザークラス
Angularアプリケーションはデータベース内のUserエンティティからフェッチして永続化するため、TypeScriptを使用して単純なドメインモデルを実装しましょう。
ターミナルコンソールを開いて、modelディレクトリを作成しましょう。
ng generate class user
AngularCLIは空のUserクラスを生成するので、いくつかのフィールドを入力してみましょう。
export class User {
id: string;
name: string;
email: string;
}
3.7. UserServiceサービス
クライアント側のドメインUserクラスがすでに設定されているので、 http:// localhost:8080 /usersエンドポイントに対してGETおよびPOSTリクエストを実行するサービスクラスを実装できます。
これにより、RESTコントローラーへのアクセスを単一のクラスにカプセル化できるようになり、アプリケーション全体で再利用できます。
コンソールターミナルを開いて、 service ディレクトリを作成し、そのディレクトリ内に次のコマンドを発行します。
ng generate service user-service
次に、AngularCLIが作成したuser.service.ts ファイルを開いて、リファクタリングします。
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 = 'http://localhost:8080/users';
}
public findAll(): Observable<User[]> {
return this.http.get<User[]>(this.usersUrl);
}
public save(user: User) {
return this.http.post<User>(this.usersUrl, user);
}
}
UserService クラスがどのように機能するかを理解するために、TypeScriptの確かな背景は必要ありません。 簡単に言えば、SpringBootで以前に実装したRESTコントローラーAPIを使用するために必要なすべての機能を再利用可能なコンポーネント内にカプセル化します。
findAll()メソッドは、AngularのHttpClientを介してhttp:// localhost:8080 /usersエンドポイントに対してGETHTTPリクエストを実行します。 このメソッドは、Userオブジェクトの配列を保持するObservableインスタンスを返します。
同様に、 save()メソッドは、 http:// localhost:8080 /usersエンドポイントに対してPOSTHTTPリクエストを実行します。
HttpClientのリクエストメソッドでタイプUserを指定することにより、バックエンド応答をより簡単かつ効果的な方法で使用できます。
最後に、
3.8. UserListComponentコンポーネント
この場合、 UserService クラスは、RESTサービスとアプリケーションのプレゼンテーション層の間の薄い中間層です。 したがって、データベースに永続化されたUserエンティティのリストのレンダリングを担当するコンポーネントを定義する必要があります。
ターミナルコンソールを開いて、 user-list ディレクトリを作成し、ユーザーリストコンポーネントを生成してみましょう。
ng generate component user-list
Angular CLIは、ngOnInitインターフェイスを実装する空のコンポーネントクラスを生成します。 インターフェイスはフックngOnInit()メソッドを宣言します。このメソッドは、実装クラスのインスタンス化が完了した後、およびコンストラクターを呼び出した後にAngularが呼び出します。
クラスをリファクタリングして、コンストラクターでUserServiceインスタンスを取得できるようにします。
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;
});
}
}
UserListComponentクラスの実装はかなり自明です。 UserServiceのfindAll()メソッドを使用して、データベースに永続化されているすべてのエンティティをフェッチし、それらをusersフィールドに格納するだけです。
さらに、コンポーネントのHTMLファイル user-list.component.html、を編集して、エンティティのリストを表示するテーブルを作成する必要があります。
<div class="card my-5">
<div class="card-body">
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of users">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td><a href="mailto:{{ user.email }}">{{ user.email }}</a></td>
</tr>
</tbody>
</table>
</div>
</div>
さらに、各ユーザーの id、 name 、およびemailを表示するために変数補間を使用しました。
3.9. UserFormComponentコンポーネント
同様に、データベースに新しいUserオブジェクトを永続化できるコンポーネントを作成する必要があります。
user-form ディレクトリを作成し、次のように入力してみましょう。
ng generate component user-form
次に、 user-form.component.ts ファイルを開き、UserFormComponentクラスにUserオブジェクトを保存するためのメソッドを追加しましょう。
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']);
}
}
この場合、 UserFormComponent は、コンストラクターで UserService インスタンスも受け取ります。これは、 onSubmit()メソッドが提供されたUserを保存するために使用します。物体。
新しいエンティティを永続化したら、更新されたエンティティのリストを再表示する必要があるため、挿入後に gotoUserList()メソッドを呼び出します。これにより、ユーザーは /usersにリダイレクトされます。 ] 道。
さらに、 user-form.component.html ファイルを編集し、データベースに新しいユーザーを永続化するためのHTMLフォームを作成する必要があります。
<div class="card my-5">
<div class="card-body">
<form (ngSubmit)="onSubmit()" #userForm="ngForm">
<div class="form-group">
<label for="name">Name</label>
<input type="text" [(ngModel)]="user.name"
class="form-control"
id="name"
name="name"
placeholder="Enter your name"
required #name="ngModel">
</div>
<div [hidden]="!name.pristine" class="alert alert-danger">Name is required</div>
<div class="form-group">
<label for="email">Email</label>
<input type="text" [(ngModel)]="user.email"
class="form-control"
id="email"
name="email"
placeholder="Enter your email address"
required #email="ngModel">
<div [hidden]="!email.pristine" class="alert alert-danger">Email is required</div>
</div>
<button type="submit" [disabled]="!userForm.form.valid"
class="btn btn-info">Submit</button>
</form>
</div>
</div>
一見、フォームはかなり標準的に見えますが、Angularの多くの機能を舞台裏でカプセル化しています。
ngSubmitディレクティブの使用に注意してください。このディレクティブは、フォームが送信されたときにonSubmit()メソッドを呼び出します。
次に、テンプレート変数#userFormを定義したので、Angularは自動的にNgFormディレクティブを追加します。これにより、フォーム全体を追跡できます。
NgForm ディレクティブは、ngModelディレクティブとname属性を持つフォーム要素用に作成したコントロールを保持します。 また、状態などのプロパティも監視します。
ngModelディレクティブは、フォームコントロールとクライアント側ドメインモデルであるUserクラスの間の双方向のデータバインディング機能を提供します。
これは、フォーム入力フィールドに入力されたデータがモデルに流れること、およびその逆を意味します。 両方の要素の変更は、DOM操作によってすぐに反映されます。
さらに、 ngModel を使用すると、各フォームコントロールの状態を追跡し、各コントロールに異なるCSSクラスとDOMプロパティを追加することでクライアント側の検証を実行できます。
上記のHTMLファイルでは、フォームコントロールに適用されたプロパティを使用して、フォームの値が変更されたときにアラートボックスを表示するだけでした。
3.10. app-routing.module.tsファイル
コンポーネントは単独で機能しますが、ユーザーがナビゲーションバーのボタンをクリックしたときにコンポーネントを呼び出すメカニズムを使用する必要があります。
ここでRouterModuleが機能します。 app-routing.module.ts ファイルを開いてモジュールを構成し、一致するコンポーネントにリクエストをディスパッチできるようにします。
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 { }
上記のように、 Routes配列は、ユーザーがリンクをクリックするか、ブラウザーのアドレスバーにURLを指定したときに表示するコンポーネントをルーターに指示します。
ルートは2つの部分で構成されています。
- パス–ブラウザのアドレスバーのURLと一致する文字列
- Component –ルートがアクティブ(ナビゲート)のときに作成するコンポーネント
ユーザーがクリックした場合ユーザーの一覧表示ボタンにリンクします / users パス、またはブラウザのアドレスバーにURLを入力すると、ルーターは UserListComponent コンポーネントのテンプレートファイル
同様に、ユーザーの追加ボタンをクリックすると、UserFormComponentコンポーネントがレンダリングされます。
3.11. app.module.tsファイル
次に、 app.module.ts ファイルを編集して、Angularが必要なすべてのモジュール、コンポーネント、およびサービスをインポートできるようにする必要があります。
さらに、UserServiceクラスの作成と挿入に使用するプロバイダーを指定する必要があります。 そうしないと、Angularはそれをコンポーネントクラスに注入できません。
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. アプリケーションの実行
これで、アプリケーションを実行する準備が整いました。
これを実現するには、最初にSpring Bootアプリケーションを実行して、RESTサービスが稼働し、リクエストをリッスンするようにします。
Spring Bootアプリケーションが起動したら、コマンドコンソールを開き、次のコマンドを入力します。
ng serve --open
これにより、Angularのライブ開発サーバーが起動し、http:// localhost:4200でブラウザーが開きます。
既存のエンティティを一覧表示したり、新しいエンティティを追加したりするためのボタンが付いたナビゲーションバーが表示されます。 最初のボタンをクリックすると、ナビゲーションバーの下に、データベースに保持されているエンティティのリストを含むテーブルが表示されます。 同様に、2番目のボタンをクリックすると、新しいエンティティを永続化するためのHTMLフォームが表示されます。
5. 結論
この記事では、SpringBootとAngularを使用して基本的なWebアプリケーションを構築する方法を学びました。
いつものように、この記事に示されているすべてのコードサンプルは、GitHubでから入手できます。