Spring Boot + npm + Geb で入力フォームを作ってテストする ( その49 )( 入力画面3を作成する2 )
概要
記事一覧はこちらです。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その48 )( 入力画面3を作成する ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- 入力画面3の作成
- 「アンケート」の項目には、DB に専用のテーブルを作成して、そこに登録されたデータから動的に表示する処理を実装します。
参照したサイト・書籍
- Conditionally closing tag in Thymeleaf
https://stackoverflow.com/questions/36747620/conditionally-closing-tag-in-thymeleaf
目次
- アンケートに表示する項目を定義するテーブルを Database Tools で作成する
- 作成した create table 文を Flyway 用の SQL ファイル V1__init.sql に追加する
- Doma-Gen で entity クラス, dao インターフェースを生成する
- SurveyOptionsHelper クラスを作成する
- SurveyOptionsHelper クラスのテストを作成する
- input03.html を修正する&動作確認
手順
アンケートに表示する項目を定義するテーブルを Database Tools で作成する
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その16 )( H2 Database に Flyway でテーブルを作成する ) を参考に、Tomcat を起動して Database Tools で in-memory モードの H2 Database に接続し、PUBLIC スキーマを選択してコンテキストメニューを表示した後、「New」-「Table」を選択します。
「Create New Table」ダイアログが表示されるので、以下の画像のようにテーブルを定義します。
ダイアログの下の「SQL Script」に生成された SQL は以下のようになります。
CREATE TABLE SURVEY_OPTIONS ( group_name VARCHAR(16) NOT NULL, item_value VARCHAR(1) NOT NULL, item_name VARCHAR(64) NOT NULL, item_order INT NOT NULL, CONSTRAINT PK_SURVEY_OPTIONS PRIMARY KEY (group_name, item_value) );
「Execute」ボタンをクリックするとテーブルが作成されて、Database Tools 上に SURVEY_OPTIONS テーブルが表示されます。
作成した create table 文を Flyway 用の SQL ファイル V1__init.sql に追加する
作成した create table 文とデータを投入する insert 文を src/main/resources/db/migration/V1__init.sql の中に記述します。
CREATE TABLE INQUIRY_DATA ( .......... ); CREATE TABLE SURVEY_OPTIONS ( group_name VARCHAR(16) NOT NULL, item_value VARCHAR(1) NOT NULL, item_name VARCHAR(64) NOT NULL, item_order INT NOT NULL, CONSTRAINT PK_SURVEY_OPTIONS PRIMARY KEY (group_name, item_value) ); INSERT INTO SURVEY_OPTIONS VALUES ('survey', '1', '選択肢1だけ長くしてみる', 1); INSERT INTO SURVEY_OPTIONS VALUES ('survey', '2', '選択肢2', 2); INSERT INTO SURVEY_OPTIONS VALUES ('survey', '3', '選択肢3', 3); INSERT INTO SURVEY_OPTIONS VALUES ('survey', '4', '選択肢4', 4); INSERT INTO SURVEY_OPTIONS VALUES ('survey', '5', '選択肢5が少し長い', 5); INSERT INTO SURVEY_OPTIONS VALUES ('survey', '6', '選択肢6', 6); INSERT INTO SURVEY_OPTIONS VALUES ('survey', '7', '選択肢7', 7); INSERT INTO SURVEY_OPTIONS VALUES ('survey', '8', '8', 8);
Tomcat を再起動すると SURVEY_OPTIONS テーブルが作成されてデータもセットされていることが確認できます。
Doma-Gen で entity クラス, dao インターフェースを生成する
build.gradle の domaGen タスクを変更し、Tomcat を起動した状態で entity クラス, dao インターフェースを生成します。
build.gradle を以下のように変更します。
// for Doma-Gen task domaGen { doLast { // まず変更が必要なもの def rootPackageName = 'ksbysample.webapp.bootnpmgeb' def rootPackagePath = 'src/main/java/ksbysample/webapp/bootnpmgeb' def dbUrl = 'jdbc:h2:tcp://localhost:9092/mem:bootnpmgebdb' def dbUser = 'sa' def dbPassword = '' def tableNamePattern = 'SURVEY_OPTIONS' // おそらく変更不要なもの ..........
- domaGen タスクの以下の点を変更します。
tableNamePattern
を.*
→SURVEY_OPTIONS
に変更します。
Gradle Tool Window から domaGen タスクを実行します。"BUILD SUCCESSFUL" が表示されました。
Project Tool Window を見ると SurveyOptionsDao インターフェース、SurveyOptions クラスが作成されています。
domaGen タスクの tableNamePattern
は .*
に戻します。
SurveyOptionsHelper クラスを作成する
SURVEY_OPTIONS テーブルのデータを取得して Thymeleaf テンプレートに出力するための SurveyOptionsHelper クラスを作成します。
まずは src/main/java/ksbysample/webapp/bootnpmgeb/dao/SurveyOptionsDao.java の以下の点を変更します。
@Dao @ComponentAndAutowiredDomaConfig public interface SurveyOptionsDao { /** * @param groupName groupName * @param itemValue itemValue * @return the SurveyOptions entity */ @Select SurveyOptions selectById(String groupName, String itemValue); /** * 指定されたグループ名のレコードのリストを取得する * * @param groupName グループ名 * @return {@SurveyOptions} エンティティのリスト */ @Select List<SurveyOptions> selectByGroupName(String groupName); ..........
- selectByGroupName メソッドを追加します。
src/main/resources/META-INF/ksbysample/webapp/bootnpmgeb/dao/SurveyOptionsDao/selectByGroupName.sql を新規作成し、以下の内容を記述します。
select /*%expand*/* from SURVEY_OPTIONS where GROUP_NAME = /* groupName */'survey' order by ITEM_ORDER
ksbysample.webapp.bootnpmgeb.helper の下に db パッケージを新規作成します。その下に SurveyOptionsHelper.java を新規作成し、以下の内容を記述します。
package ksbysample.webapp.bootnpmgeb.helper.db; import ksbysample.webapp.bootnpmgeb.dao.SurveyOptionsDao; import ksbysample.webapp.bootnpmgeb.entity.SurveyOptions; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.util.List; /** * SURVEY_OPTIONS テーブルデータ取得用 Helper クラス */ @Component("soh") public class SurveyOptionsHelper { private final SurveyOptionsDao surveyOptionsDao; /** * コンストラクタ * * @param surveyOptionsDao {@SurveyOptionsDao} オブジェクト */ public SurveyOptionsHelper(SurveyOptionsDao surveyOptionsDao) { this.surveyOptionsDao = surveyOptionsDao; } /** * SURVEY_OPTIONS テーブルから指定されたグループ名のリストを取得する * * @param groupName グループ名 * @return {@SurveyOptions} オブジェクトのリスト */ public List<SurveyOptions> selectItemList(String groupName) { List<SurveyOptions> surveyOptionsList = surveyOptionsDao.selectByGroupName(groupName); if (CollectionUtils.isEmpty(surveyOptionsList)) { throw new IllegalArgumentException("指定されたグループ名のデータは登録されていません"); } return surveyOptionsList; } }
SurveyOptionsHelper クラスのテストを作成する
src/main/java/ksbysample/webapp/bootnpmgeb/helper/db/SurveyOptionsHelper.java で Ctrl+Shift+T を押してコンテキストメニューを表示した後、「Create New Test...」を選択します。
src/test/groovy/ksbysample/webapp/bootnpmgeb/helper/db/SurveyOptionsHelperTest.groovy が新規作成されますので、以下の内容を記述します。
package ksbysample.webapp.bootnpmgeb.helper.db import ksbysample.webapp.bootnpmgeb.entity.SurveyOptions import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import spock.lang.Specification @SpringBootTest class SurveyOptionsHelperTest extends Specification { @Autowired private SurveyOptionsHelper soh def "登録されているグループ名を指定してselectItemListメソッドを呼ぶとリストが取得できる"() { setup: List<SurveyOptions> surveyOptionsList = soh.selectItemList("survey") expect: surveyOptionsList.size() == 8 surveyOptionsList[0].itemValue == "1" surveyOptionsList[0].itemName == "選択肢1だけ長くしてみる" surveyOptionsList[7].itemValue == "8" surveyOptionsList[7].itemName == "8" } def "登録されていないグループ名を指定してselectItemListメソッドを呼ぶとIllegalArgumentExceptionがthrowされる"() { when: List<SurveyOptions> surveyOptionsList = soh.selectItemList("notexists") then: def e = thrown(IllegalArgumentException) e.getMessage() == "指定されたグループ名のデータは登録されていません" } }
テストを実行して成功することを確認します。
input03.html を修正する&動作確認
「アンケート」の項目を SurveyOptionsHelper クラスを利用して SURVEY_OPTIONS テーブルのデータを出力するように src/main/resources/templates/web/inquiry/input03.html を以下のように変更します。
<div class="form-group" id="form-group-survey"> <div class="control-label col-sm-2"> <label class="float-label">アンケート</label> </div> <div class="col-sm-10" id="multiline-checkbox"> <th:block th:each="surveyOptions,iterStat : ${@soh.selectItemList('survey')}"> <th:block th:if="${iterStat.index % 3 == 0}"> <div class="row"><div class="col-sm-12"> <div class="checkbox"> </th:block> <label><input type="checkbox" name="survey" th:value="${surveyOptions.itemValue}"> <th:block th:text="${surveyOptions.itemName}">選択肢1だけ長くしてみる</th:block> </label> <th:block th:if="${iterStat.index % 3 == 2 || iterStat .last}"> </div> </div></div> </th:block> </th:block> </div> </div>
Tomcat を起動して入力画面3を表示してみましたが、ボタンの表示位置がおかしいですね。。。
html を見ると、最初の <th:block th:if="${iterStat.index % 3 == 0}">...</th:block>
は正常に動作していますが、後の <th:block th:if="${iterStat.index % 3 == 2 || iterStat.last}"></div>...</th:block>
が最後しか出力されておらず正常に動作していませんでした。</th:block>
も最後に2つそのまま出力されています。どうも Thymeleaf では閉じタグ側を動的に出力させる書き方ができないようです。
<div class="form-group" id="form-group-survey"> <div class="control-label col-sm-2"> <label class="float-label">アンケート</label> </div> <div class="col-sm-10" id="multiline-checkbox"> <div class="row"><div class="col-sm-12"> <div class="checkbox"> <label><input type="checkbox" name="survey" value="1"> 選択肢1だけ長くしてみる </label> <label><input type="checkbox" name="survey" value="2"> 選択肢2 </label> <label><input type="checkbox" name="survey" value="3"> 選択肢3 </label> <div class="row"><div class="col-sm-12"> <div class="checkbox"> <label><input type="checkbox" name="survey" value="4"> 選択肢4 </label> <label><input type="checkbox" name="survey" value="5"> 選択肢5が少し長い </label> <label><input type="checkbox" name="survey" value="6"> 選択肢6 </label> <div class="row"><div class="col-sm-12"> <div class="checkbox"> <label><input type="checkbox" name="survey" value="7"> 選択肢7 </label> <label><input type="checkbox" name="survey" value="8"> 8 </label> </div> </div></div> </th:block> </th:block> </div>
stackoverflow で調べてみると Conditionally closing tag in Thymeleaf というページを見つけました。今回は th:utext を使用する方法に変更してみます。
src/main/resources/templates/web/inquiry/input03.html を以下のように変更します。
<div class="form-group" id="form-group-survey"> <div class="control-label col-sm-2"> <label class="float-label">アンケート</label> </div> <div class="col-sm-10" id="multiline-checkbox"> <th:block th:each="surveyOptions,iterStat : ${@soh.selectItemList('survey')}"> <th:block th:if="${iterStat.index % 3 == 0}" th:utext="'<div class="row"><div class="col-sm-12"><div class="checkbox">'"/> <label><input type="checkbox" name="survey" th:value="${surveyOptions.itemValue}"> <th:block th:text="${surveyOptions.itemName}">選択肢1だけ長くしてみる</th:block> </label> <th:block th:if="${iterStat.index % 3 == 2 || iterStat.last}" th:utext="'</div></div></div>'"/> </th:block> </div> </div>
- 上下の条件を満たした時に div タグを出力する処理を
<th:block th:if="..." th:utext="..."/>
の形式に変更します。 th:utext
の中に書く HTML 文は<
→<
へ、>
→>
へ、”
→"
へ変換します。
今度は画面も問題なく表示されました。
html を見てもきちんと閉じタグが出力されています。
<div class="form-group" id="form-group-survey"> <div class="control-label col-sm-2"> <label class="float-label">アンケート</label> </div> <div class="col-sm-10" id="multiline-checkbox"> <div class="row"><div class="col-sm-12"><div class="checkbox"> <label><input type="checkbox" name="survey" value="1"> 選択肢1だけ長くしてみる </label> <label><input type="checkbox" name="survey" value="2"> 選択肢2 </label> <label><input type="checkbox" name="survey" value="3"> 選択肢3 </label> </div></div></div> <div class="row"><div class="col-sm-12"><div class="checkbox"> <label><input type="checkbox" name="survey" value="4"> 選択肢4 </label> <label><input type="checkbox" name="survey" value="5"> 選択肢5が少し長い </label> <label><input type="checkbox" name="survey" value="6"> 選択肢6 </label> </div></div></div> <div class="row"><div class="col-sm-12"><div class="checkbox"> <label><input type="checkbox" name="survey" value="7"> 選択肢7 </label> <label><input type="checkbox" name="survey" value="8"> 8 </label> </div></div></div> </div> </div>
履歴
2018/04/25
初版発行。