Spring BootのWebアプリケーションの応用④〜Bootstrap~

<a href="https://qiita.com/homes31285/items/5393b30cdfd9741f604a">前回の記事</a>では、注文フォームの更新と削除の機能を追加しました。

今回は、Bootstrapを使って、フロント側の修正を行なっていきます。

<details>
<summary>ファイル構成</summary>
<pre>
<code>
.
├── .gradle
├── .idea
├── build
├── gradle
└── src
     ├── main
     |      ├── java
     |      |      └── com
     |      |           └── example
     |      |                 └── practice 
     |      |                          PracticeApplication.java
     |      |                          ├── web 
     |      |                          |    ├── order
     |      |                          |    |    ├── OrderForm.java
     |      |                          |    |      └── OrderController.java
     |      |                          |    |                
     |      |                          |    └── IndexController.java
     |      |                          └── domain
     |      |                                 └── order 
     |      |                                       └── OrderEntity.java
     |      |                                       └── OrderService.java
     |      |                                       └── OrderRepository.java
     |      |                               
     |      └── resources
     |              ├── static
     |              ├── templates
     |              |        └── order
     |              |                           
     ├──delete_confirmation.html
     |              |                  ├── detail.html 
     |              |                  ├── form.html 
     |              |                  └── list.html
     |              └── index.html
     |              schema.sql
     |              data.sql 
     | 　　　　　　　　　　　　       application.properties
     └── test
.gitignore
build.gradle
gradlew.bat
HELP.md
settings.gradle
</code>
</pre>
</details>

<h2>Bootstrap</h2>

Bootstrapは、TwitterのMark OttoとJacob Thorntonによって2011年に公開されました。

彼らの目的は、インターフェースの一貫性を確保しつつ、開発者が迅速に高品質なコードを生成できるツールを提供することでした。

彼らの取り組みは、瞬く間にデベロッパーコミュニティに受け入れられ、現在では最も人気のあるフロントエンドフレームワークの一つとなっています。

<h3>Bootstrapの主要な特徴</h3>

Bootstrapの主要な特徴

<b>レスポンシブデザイン</b>: 今日のインターネット利用者は、さまざまなデバイスを使用してオンラインにアクセスしています。Bootstrapは、12カラムのグリッドシステムを使用して、画面サイズに関係なくコンテンツが適切に表示されるように設計されています。

<b>再利用可能なコンポーネント</b>: Bootstrapの美しいことは、その再利用可能なUIコンポーネントにあります。ボタン、ナビゲーション、カルーセルなど、多くの要素が事前にスタイル付けされており、カスタムデザインが容易になります。

<b>JavaScriptの魔法</b>: Bootstrapは、動的な要素を追加するための多数のJavaScriptプラグインを提供しています。これにより、モーダルウィンドウやスライドダウンメニューなどの機能を簡単に追加できます。

<b>カスタマイズ</b>: Bootstrapはモジュール式であり、開発者は自分のニーズに合わせて特定のコンポーネントを追加または削除することができます。これは、特定のプロジェクトに必要なものだけを選び、不要なものは排除することを可能にします。

早速、BootStrapでhtmlを修正していきましょう。

<b>トップページ</b>


```html:index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>トップページ | 業務用アプリケーション</title>
    <!-- Bootstrap CSS link -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-4">
    <h1 class="mb-3"><a href="../index.html" th:href="@{/}" style="color: inherit; text-decoration: none;">業務管理アプリケーション</a></h1>
    <ul class="list-group">
        <li class="list-group-item"><a href="./order/list.html" th:href="@{/orders}" class="btn btn-primary">注文リスト一覧</a></li>
    </ul>
</div>
</body>
</html>
```
<b>注文リスト</b>

```html:list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>注文リスト | 業務用アプリケーション</title>
    <!-- Bootstrap CSS link -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-4">
    <h1 class="mb-3"><a href="../index.html" th:href="@{/}" style="color: inherit; text-decoration: none;">業務管理アプリケーション</a></h1>
    <h2 class="text-center">注文リスト</h2>
    <a href="../index.html" th:href="@{/}" class="btn btn-primary">トップページ</a>
    <a href="./form.html" th:href="@{/orders/form}" class="btn btn-success">作成</a>

    <table class="table mt-4">
        <thead class="thead-dark">
        <tr>
            <th>注文番号</th>
            <th>納入日付</th>
            <th>会社番号</th>
            <th>会社名</th>
            <th>品物番号</th>
            <th>品物</th>
            <th>数量</th>
            <th>単価</th>
            <th>金額</th>
        </tr>
        </thead>
        <tbody>
        <!-- orderList の内容をループして表示 -->
        <tr th:each="order : ${orderList}">
            <td>
                <a href="./detail.html" th:href="@{/orders/{orderId}(orderId=${order.orderId})}" th:text="${order.orderId}" class="btn btn-link">
                    (orderId)
                </a>
            </td>
            <td th:text="${order.orderDate}"></td>
            <td th:text="${order.companyNo}"></td>
            <td th:text="${order.companyName}"></td>
            <td th:text="${order.itemNo}"></td>
            <td th:text="${order.item}"></td>
            <td th:text="${order.quantity}"></td>
            <td th:text="${order.unitPrice}"></td>
            <td th:text="${order.price}"></td>
        </tr>
        </tbody>
    </table>
</div>
</body>
</html>
```

<b>注文作成フォーム</b>

```html:form.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>注文作成フォーム | 業務管理アプリケーション</title>
    <!-- Bootstrap CSS link -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-4">
    <div class="row justify-content-center">
        <div class="col-md-8">
     <h1 class="text-center">注文作成フォーム</h1>
            <form th:action="@{/orders}" th:method="post" th:object="${orderForm}" class="mt-4">
                <div class="form-group">
                    <label>注文番号</label>
                    <span th:text="${nextOrderId}" class="form-control-plaintext"></span>
                </div>
                <div class="form-group">
                    <label for="orderDate">納入日付</label>
                    <input type="date" id="orderDate" th:field="*{orderDate}" class="form-control">
                    <p th:if="${#fields.hasErrors('orderDate')}" th:errors="*{orderDate}" class="text-danger"></p>
                </div>
                <div class="form-group">
                    <label for="companyNo">会社番号</label>
                    <input type="number" id="companyNo" th:field="*{companyNo}" class="form-control">
                    <p th:if="${#fields.hasErrors('companyNo')}" th:errors="*{companyNo}" class="text-danger"></p>
                </div>
                <div class="form-group">
                    <label for="companyName">会社名</label>
                    <input type="text" id="companyName" th:field="*{companyName}" class="form-control">
                    <p th:if="${#fields.hasErrors('companyName')}" th:errors="*{companyName}" class="text-danger"></p>
                </div>
                <div class="form-group">
                    <label for="itemNo">品物番号</label>
                    <input type="number" id="itemNo" th:value="*{itemNo}" class="form-control">
                    <p th:if="${#fields.hasErrors('itemNo')}" th:errors="*{itemNo}" class="text-danger"></p>
                </div>
                <div class="form-group">
                    <label for="item">品物</label>
                    <input type="text" id="item" th:field="*{item}" class="form-control">
                    <p th:if="${#fields.hasErrors('item')}" th:errors="*{item}" class="text-danger"></p>
                </div>
                <div class="form-group">
                    <label for="quantity">数量</label>
                    <input type="number" id="quantity" th:field="*{quantity}" class="form-control">
                    <p th:if="${#fields.hasErrors('quantity')}" th:errors="*{quantity}" class="text-danger"></p>
                </div>
                <div class="form-group">
                    <label for="unitPrice">単価</label>
                    <input type="number" id="unitPrice" th:field="*{unitPrice}" class="form-control">
                    <p th:if="${#fields.hasErrors('unitPrice')}" th:errors="*{unitPrice}" class="text-danger"></p>
                </div>
                <div class="form-group">
                    <label for="price">金額</label>
                    <input type="number" id="price" th:field="*{price}" class="form-control">
                    <p th:if="${#fields.hasErrors('price')}" th:errors="*{price}" class="text-danger"></p>
                </div>
                <div class="form-group text-center">
                    <button type="submit" class="btn btn-primary">作成</button>
                </div>
            </form>
            <div class="form-group text-center mt-2">
                <a th:href="@{/orders}" class="btn btn-secondary">取消</a>
            </div>
        </div>
    </div>
</div>
</body>
</html>
```
<b>注文リスト詳細フォーム</b>

```html:detail.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>注文リスト詳細フォーム | 業務管理アプリケーション</title>
    <!-- Bootstrap CSS link -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-4">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <h1 class="text-center">注文リスト詳細フォーム</h1>
            <form th:object="${order}" th:action="@{/orders/update}" method="post">
                <div class="form-group">
                    <input type="hidden" th:field="*{orderId}">
                    <label>注文番号</label>
                    <span th:text="${order.orderId}" class="form-control-plaintext"></span>
                </div>
                <div class="form-group">
                    <label for="orderDate">納入日付</label>
                    <input type="date" id="orderDate" th:field="*{orderDate}" class="form-control">
                    <p th:if="${#fields.hasErrors('orderDate')}" th:errors="*{orderDate}" class="text-danger"></p>
                </div>
                <div class="form-group">
                    <label for="companyNo">会社番号</label>
                    <input type="number" id="companyNo" th:field="*{companyNo}" class="form-control">
                    <p th:if="${#fields.hasErrors('companyNo')}" th:errors="*{companyNo}" class="text-danger"></p>
                </div>
                <div class="form-group">
                    <label for="companyName">会社名</label>
                    <input type="text" id="companyName" th:field="*{companyName}" class="form-control">
                    <p th:if="${#fields.hasErrors('companyName')}" th:errors="*{companyName}" class="text-danger"></p>
                </div>
                <div class="form-group">
                    <label for="itemNo">品物番号</label>
                    <input type="number" id="itemNo" th:value="*{itemNo}" class="form-control">
                    <p th:if="${#fields.hasErrors('itemNo')}" th:errors="*{itemNo}" class="text-danger"></p>
                </div>
                <div class="form-group">
                    <label for="item">品物</label>
                    <input type="text" id="item" th:field="*{item}" class="form-control">
                    <p th:if="${#fields.hasErrors('item')}" th:errors="*{item}" class="text-danger"></p>
                </div>
                <div class="form-group">
                    <label for="quantity">数量</label>
                    <input type="number" id="quantity" th:field="*{quantity}" class="form-control">
                    <p th:if="${#fields.hasErrors('quantity')}" th:errors="*{quantity}" class="text-danger"></p>
                </div>
                <div class="form-group">
                    <label for="unitPrice">単価</label>
                    <input type="number" id="unitPrice" th:field="*{unitPrice}" class="form-control">
                    <p th:if="${#fields.hasErrors('unitPrice')}" th:errors="*{unitPrice}" class="text-danger"></p>
                </div>
                <div class="form-group">
                    <label for="price">金額</label>
                    <input type="number" id="price" th:field="*{price}" class="form-control">
                    <p th:if="${#fields.hasErrors('price')}" th:errors="*{price}" class="text-danger"></p>
                </div>
                <div class="form-group text-center">
                    <button type="submit" class="btn btn-primary">更新</button>
                    <a th:href="@{/orders/delete/{orderId}(orderId=${order.orderId})}" class="btn btn-danger">削除</a>
                    <a href="./order/list.html" th:href="@{/orders}" class="btn btn-secondary">取消</a>
                </div>
            </form>
        </div>
    </div>
</div>
</body>
</html>
```

<b>注文リスト削除確認</b>

```html:delete_confirmation.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>注文リスト削除確認| 業務管理アプリケーション</title>
  <!-- Bootstrap CSS link -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-4">
  <h1 class="mb-3 text-center">注文リスト削除確認</h1>
  <p class="text-center">以下の注文を削除しますか？</p>

  <div class="row">
    <div class="col-md-3 offset-md-3"><strong>注文番号:</strong></div>
    <div class="col-md-4" th:text="${order.orderId}"></div>
  </div>

  <div class="row">
    <div class="col-md-3 offset-md-3"><strong>納入日付:</strong></div>
    <div class="col-md-4" th:text="${order.orderDate}"></div>
  </div>

  <div class="row">
    <div class="col-md-3 offset-md-3"><strong>会社番号:</strong></div>
    <div class="col-md-4" th:text="${order.companyNo}"></div>
  </div>

  <div class="row">
    <div class="col-md-3 offset-md-3"><strong>品物番号:</strong></div>
    <div class="col-md-4" th:text="${order.itemNo}"></div>
  </div>

  <div class="row">
    <div class="col-md-3 offset-md-3"><strong>品物:</strong></div>
    <div class="col-md-4" th:text="${order.item}"></div>
  </div>

  <div class="row">
    <div class="col-md-3 offset-md-3"><strong>数量:</strong></div>
    <div class="col-md-4" th:text="${order.quantity}"></div>
  </div>

  <div class="row">
    <div class="col-md-3 offset-md-3"><strong>単価:</strong></div>
    <div class="col-md-1" th:text="${order.unitPrice}"></div>
  </div>

  <div class="row">
    <div class="col-md-3 offset-md-3"><strong>金額:</strong></div>
    <div class="col-md-1" th:text="${order.price}"></span></div>
  </div>

  <form class="text-center mt-3" th:action="@{/orders/delete/{orderId}(orderId=${order.orderId})}" method="post">
    <button type="submit" class="btn btn-danger">削除</button>
    <a th:href="@{/orders}" class="btn btn-secondary ml-2">取消</a>
  </form>
</div>
</body>
</html>
```


<h2>まとめ</h2>

Bootstrapの特徴や各ページのコード例を通じて、どのようにフロントエンドを整備するかをコードを通じて説明をしました。