SpringBootでとりあえず業務アプリを自作してみた ～TOP・ユーザ検索編～

前回までで、作成したアプリをWEBアプリとして公開しました。

その後、少し機能を追加してみたのでまとめます。


トップ画面
--
ユーザの登録機能を作成しました。
最初はログイン画面に設置していましたが
ログイン後の画面に設置しなおしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901612/4039135c-9300-fbe0-dc8e-fbec63995467.png)

トップページにはまだ何も置いていません。
お知らせ機能とか、カレンダーとか置きたいなと思っていますが
これから徐々に機能を増やしていきたいなと思っています。

前回までの記事でも記載しましたがログイン機能はSpringSecurityを使用しました。

手を動かしてコーディングをするのが数年ぶりなのと
SpringBootでアプリを作成するのが初めてなので
色々探り探りで躓きながらですが作成を進めています。

右上の「山田 太郎」はcontrollerから受け取ったログインユーザの名前が出るようにしています。

以下の記事の後ろのほうに記載したcssが適用されない件ですが解決しました。

https://qiita.com/mSaito0217/items/e1bc58b451a5b050e01e

当初は以下のような階層にしていたのですが
```
└── src
   └── main
     ├── java
     └── resources
       └── static
        └── templates
          └── index.html // cssを適用させたいhtml
          └── css
            └── style.html
```
このようにしたら読み込めるようになりました。
```
└── src
   └── main
     ├── java
     └── resources
       └── static
        ├── templates
        | └── index.html // cssを適用させたいhtml
        └── css
            └── style.html
```

Thymeleafを使用しているので、記述方式がThymeleaf式です。
ちなみに
th:src … HTML 文書中に埋め込む画像やスクリプトなどのリソース
th:href … HTML 文書にリンクするリソース
という区別だそうです。

画面側はあまりやってこなかったので勉強になりました。

同じように右上のアイコン画像も読み込めたので
ログアウト用のアイコンとして設置しています。

```html:index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
	<meta charset="UTF-8">
	<link th:href="@{/css/style.css}" rel="stylesheet">
	<script th:src="@{https://code.jquery.com/jquery-3.4.1.min.js}"></script>
</head>

<body class="in-body">

<!-- 以下略 -->
```
ヘッダー部分を共通化したかったのですが、こちらはうまくいきませんでした。
どうにもHTMLがうまく読み込まれない...
またしばらくしたらトライしたいです。

ユーザ検索画面
--
登録したユーザ情報が検索できる画面を作成しました。
メニューの「ユーザ管理」から遷移する先がこちらです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901612/5018f53e-1f76-d622-47ae-2f9e132440c1.png)

全件検索が以下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901612/13991b7c-3c22-5dee-ae2b-81eb78d6d000.png)

テーブルはjQuaryで作成するか迷いましたが
Thymeleafの機能で作成しました。便利です。

ソースコードはこんな感じ。

```java:UserController.java
/**
	 * ユーザ検索画面の処理（GET）
	 * @return
	 */
	@GetMapping("/userSearch")
	public ModelAndView userSearchForm(@ModelAttribute UserSearchDto userSearchDto) {
		// 画面に返却するオブジェクトを生成
		ModelAndView mav = new ModelAndView();
		// ログイン情報を取得
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		UserPrincipal userInfo = (UserPrincipal) authentication.getPrincipal();

		if (userInfo != null) {
			// ユーザ情報が取得できた場合は名前をセット
			mav.addObject("name", userInfo.getUser().getLastName() + " " + userInfo.getUser().getFirstName());
		}

		if (userSearchDto.getViewType() == null) {
			// 初期表示処理
			mav.addObject("userSearchDto", new UserSearchDto());
			mav.setViewName("userSearch");
			return mav;
		}

		// 検索実行時処理
		List<User> users = null;

		if (userSearchDto.getUserId().isBlank() &&
				userSearchDto.getFirstName().isBlank() &&
				userSearchDto.getFirstNameKana().isBlank() &&
				userSearchDto.getLastName().isBlank() &&
				userSearchDto.getLastNameKana().isBlank() &&
				userSearchDto.getRoleAdmin() == 0 &&
				userSearchDto.getRoleEmployee() == 0 &&
				userSearchDto.getRoleContract() == 0 &&
				userSearchDto.getValid() == 0) {
			// 全件検索
			users = userService.findAll();
		} else {
			// 部分検索
			users = userDaoImpl.search(userSearchDto);
		}

		mav.addObject("userSearchDto", new UserSearchDto()); // 次の検索実行用の入れ物

		if (users.isEmpty()) {
			// ユーザ情報が1件も取得できなかった場合
			mav.addObject("msg", CommonVariable.MSG_02);
		} else {
			mav.addObject("users", users);
		}

		mav.setViewName("userSearch");
		return mav;
	}

```

```html:userSearch.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
	<meta charset="UTF-8">
	<link th:href="@{/css/style.css}" rel="stylesheet">
	<script th:src="@{https://code.jquery.com/jquery-3.4.1.min.js}"></script>
</head>

<body class="in-body">

	<!--ヘッダー共通部分（略） -->

	<main>
		<p class="view-title">ユーザ検索</p>
		<div class="search-form">
			<form th:action="@{/userSearch}" th:object="${userSearchDto}" method="get">
				<label class="search-label">ユーザID</label>
				<input type="text" id="userId" name="userId"><br>
				<label class="search-label">ユーザ名</label>
				<input type="text" id="lastName" name="lastName" placeholder="姓">
				<input type="text" id="firstName" name="firstName" placeholder="名"><br>
				<label class="search-label">ユーザ名(カナ)</label>
				<input type="text" id="lastNameKana" name="lastNameKana" placeholder="セイ">
				<input type="text" id="firstNameKana" name="firstNameKana" placeholder="メイ"><br>
				<label for="email" class="search-label">メールアドレス</label>
				<input type="text" id="email" name="email"><br>
				<label class="search-label">権限</label>
				<input type="checkbox" name="roleAdmin" value="1" /><label>管理者</label>
				<input type="checkbox" name="roleEmployee" value="1" /><label>社員</label>
				<input type="checkbox" name="roleContract" value="1" /><label>契約社員</label><br>
				<label class="search-label">有効/無効</label>
				<input type="checkbox" id="valid" name="valid" value="1" /><label for="valid">無効を含む</label><br>
				<input type="hidden" name="viewType" value="search" />
				<button class="submit" type="submit" value="Submit">検索</button>
				<button class="reset" type="reset" value="Reset">リセット</button>
				<button class="register" type="register" value="Register"><a th:href="@{/userRegister}">新規登録</a></button>
			</form>
		</div>
		<a class="search-msg" th:if="${msg}" th:text="${msg}"></a>
		<table>
			<tr th:if="${users}">
				<th>ユーザID</th>
				<th id="fix">名</th>
				<th id="fix">姓</th>
				<th id="fix">メールアドレス</th>
				<th id="fix">権限</th>
				<th id="fix">有効/無効</th>
				<th></th>
			</tr>
			<tr class="data-table" th:each="item: ${users}">
				<td class="fix" th:text="${item.userId}"></td>
				<td class="fix" th:text="${item.lastName}"></td>
				<td class="fix" th:text="${item.firstName}"></td>
				<td class="fix" th:text="${item.email}"></td>
				<td class="fix" th:if="${item.roleCode} eq 0">システム管理者</td>
				<td class="fix" th:if="${item.roleCode} eq 1">管理者</td>
				<td class="fix" th:if="${item.roleCode} eq 2">社員</td>
				<td class="fix" th:if="${item.roleCode} eq 3">契約社員</td>
				<td class="fix" th:if="${item.validFlag} eq true">有効</td>
				<td class="fix" th:if="${item.validFlag} eq false">無効</td>
				<td>
					<a th:href="@{/userRef(userId=${item.userId})}"><img class="edit" th:src="@{/img/edit.png}" height="20px"></a>
				</td>
			</tr>
		</table>
	</main>
	<footer></footer>
</body>
<script th:src="@{/js/common.js}"></script>
</html>
```
>th:each="item: ${users}"

これが便利です。
usersにはControllerから渡されたUserのリストが格納されており
これが存在した場合はリストの数だけ次のコードの生成を行ってくれます。

ログイン情報の取得方法についてや初期表示の判定方法は
他にもっと効率的な処理の仕方があるような気がしますが
技術不足があるので勉強しつつでアップデートしていけたらなと思います。

HTMLは基本的にノリで書いているので
フロントエンジニアから見るとダメダメかもしれません。
idとclassの使い分けとか、divタグ aタグ pタグの使い分けとかあんまりよくわかってません...
classはグループ名でidは項目単位の名前ってくらいの認識。

検索ボタンからは検索画面に、
新規登録ボタンからは新規登録画面に
テーブルの行の一番右のアイコンからは編集画面に遷移します。
リセットの処理はjQuaryで実装したいですがまだできていません。

組みながら最善を模索していますので
javaでもhtmlでも、こんな方法があるよ！こうしたらもっといいよ！
など、アドバイス等あればコメントいただけるとと思います。

思ったより長くなってしまったので今回は一旦ここまで、
次回、検索処理の詳細と、新規登録・編集の処理について書いていきたいと思います。