目次

作成するもの

SpringBootで、追加ボタンをクリックすると、入力フォームが増えるような、動的フォームを作成します
jQueryのajaxとThymleafのfragmentを使用して、画面遷移をせず画面の一部のみ更新するようにしています

完成したもの


プロジェクトの作成

使用するもの

プロジェクトを作成する前に今回私が使用するライブラリやIDEとバージョンを載せておきます
  • Eclipse 2022-06
  • jQuery 3.6.0

Spring Initializerを使用して、プロジェクトを作成していきます
SpringInitializrは条件に合わせてSpringBootプロジェクトの雛形を生成するWebサービスです
【URL】 https://start.spring.io/

必要最低限のライブラリとして、SpringWebとThymeleafをDependenciesに追加しています
その他デフォルトのままで

GENERATEボタンをクリックすると、zipファイルがダウンロードされます
解凍してEclipseにインポートします

フォームの作成

まずは、通常の入力フォームと、入力したデータをコントローラで受け取り、結果を画面表示できるようにします
特に、変なことはしていないので、コードだけ載せておきます



動作確認



fragmentの導入、入力フォームの追加(まだ動的フォームにはしない)

fragmentとは、th:fragment 属性を指定した要素を、別の箇所に th:insert や th:replaceで埋め込むことができる機能です
fragmentを利用し、動的フォーム部分のhtmlを部品化していきます

修正したコードのみ載せます

現状のフォルダ構成
demo
        > src/main/java
          > com.example.demo
            > controller
              > PersonForm.java
              > ProgrammingLanguage.java  ←新規作成
              > WelcomeController.java
            > DemoApplication.java
        > src/main/resources
          > static
          > templates
            > fragments.html  ←新規作成
            > index.html
            > result.html
          > application.properties
        // 以下省略

PersonForm.java

まずは、フォームクラスを修正して、フィールドを追加します
動的フォーム(入力数が可変)のため、Listにしておきます
public class PersonForm {
	private String name;    
    private <ListProgrammingLanguage> programmingLanguageList;
        
    public PersonForm() {
        programmingLanguageList = new ArrayList();
        programmingLanguageList.add(new ProgrammingLanguage());
    }
}

ProgrammingLanguage.java

続いて、先ほど追加したListの要素となるクラスです
public class ProgrammingLanguage {
	private String name;
	private Integer yearsOfExperience;
    // GetterSetter省略
}

fragments.html

動的フォームにする部分はfragmentを使用して別のhtmlファイルに記述します
<!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <!-- th:framnetでfragment名を指定します -->
    <div id="programmingLanguage" th:fragment="programmingLanguage"  th:object="${personForm}">
        <div th:each="programmingLanguage, stat : *{programmingLanguageList}">
            プログラミング言語
            <!-- __${}__ で囲ったところがthymelafによって先に評価されます-->
            <input type="text" th:field="*{programmingLanguageList[__${stat.index}__].name}">
            実務経験
            <input type="number" th:field="*{programmingLanguageList[__${stat.index}__].yearsOfExperience}">
            年
        </div>
     </div>
</html>

index.html

次に、index.htmlの修正です
<th:block>…</th:block>の部分がfragments.htmlの<div th:fragment=”programmingLanguage”>…</div>に置き換わります
<body>
    <form id="form" th:action="@{/result}" method="post" th:object="${personForm}">
        <div>
            名前:<input type="text" th:field="*{name}">
            <!-- th:replaceにはファイル名::fragment名を指定 -->
            <th:block th:replace="fragments::programmingLanguage"></th:block>
        </div>
        <input type="submit" value="送信">
    </form>
</body>

result.html

結果表示画面も微修正します
<body>
	<div th:object="${personForm}">
		名前:<span th:text="*{name}"></span> <br>
		<th:block th:each="programmingLanguage, stat : *{programmingLanguageList}">
			プログラミング言語:<span th:text="*{programmingLanguageList[__${stat.index}__].name}"></span>
			実務経験:<span th:text="*{programmingLanguageList[__${stat.index}__].yearsOfExperience}"></span>年
            <br>
		</th:block>
	</div>
</body>

動作確認



ajaxと動的フォームの実装

追加ボタンの追加、追加ボタンクリックで入力フォームが増えるようにします

fragment.html

まずは、fragment.htmlを修正していきます
変更点としては、追加ボタンを追記しています
inputのtypeはsubmitではなくbuttonとし、onclickにこれから実装するfunction名を指定します
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div id="programmingLanguage" th:fragment="programmingLanguage" th:object="${personForm}">
   <div th:each="programmingLanguage, stat : *{programmingLanguageList}">
        プログラミング言語
        <input type="text" th:field="*{programmingLanguageList[__${stat.index}__].name}">
        実務経験
        <input type="number" th:field="*{programmingLanguageList[__${stat.index}__].yearsOfExperience}">年
    </div>
    <input type="button" onclick="addForm()" value="追加">
</div>
</html>

index.html

追加ボタンが押された時の処理を記載していきます
<body>
<form id="form" th:action="@{/result}" method="post" th:object="${personForm}">
    <!-- 変更点ないため省略-->
</form>
<!-- jQueryはCDNで利用します CDNのコードは以下サイトから取得できます
    Slim版ではAjaxが利用できないので、通常版を使用してください
    
jQuery CDN – Latest Stable Versions
--> <script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"> </script> <script> function addForm(){ $.ajax({ url:"/addForm", // リクエストの送信先 type:"POST", // 使用するHTTPメソッドのタイプ data:$("#form").serialize(), // 送信するデータ (id="form"内のデータを取得している) dataType:"html", // 応答のデータの種類 }).done(function(data){ // 通信成功時の処理 $("#programmingLanguage").html(data); // id="programmingLanguage"内のhtmlを応答データに置き換えている }); } </script> </body>

WelcomeController.java

追加ボタンをクリックすることで、/addFormにリクエストが送信されるので、コントローラーの処理を追加する
@Controller
public class WelcomeController {
    //省略
    
    // 以下メソッドを追加
    @PostMapping("/addForm")
    public String addForm(@ModelAttribute("personForm")PersonForm personForm) {
        // programmingLanguageListの要素を1つ増やすメソッドを呼んでいる(この後実装する)
        personForm.addForm();
        // returnにはfragmentを指定する
        return "fragments::programmingLanguage";
    }
}

PersonForm.java

public class PersonForm {
	// 省略
	
    // 以下のメソッドを追加する
	public void addForm() {
		programmingLanguageList.add(new ProgrammingLanguage());
	}
}

動作確認

お疲れ様でした