Thymeleaf 備忘録

## thymeleafの宣言
```
<html xmlns:th="http://www.thymeleaf.org">
```

## コントローラーからビューに値を渡す方法

### コントローラー側
```:DemoContoroller.java
@GetMapping("/")
public String getMenu(Model model){	     
  //String型の例
  String str1 ="SpringBoot練習プロジェクト";
  
　//クラス型の例
  SampleClass sampleClass = new SampleClass(); 
　sampleClass.setName("SpringBoot練習プロジェクト_その2")
  
  model.addAttribute("str2", str1);		 
  model.addAttribute("sampleClass", sampleClass ); 

  return "index.html";	        
}
```
|ポイント|説明|
|----|----|
|Model型、ModelMap型|どちらを使用してもOK。||
|addAttribute(String attributeName, Object attributeValue)|"str2":Thymeleaf側<br>str1:コントローラー側||
|addAttribute(Object attributeValue)|似たようなメソッドでオブジェクトを渡すタイプもあるが、もしこれを使って「model.addAttribute(sampleClass );」と記述しても<b>thymeleaf側で受け取れないので注意。</b>|
【公式】https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/ui/Model.html


### ビュー側
```:demo_view.html
<HTML>
<!--中略-->

<!--String型の記述例-->
 <div th:text=${str2}></div>

<!--クラス型の記述例1-->
 <p th:text="${sampleClass.name}">

<!--クラス型の記述例2-->
 <div th:object="${sampleClass}">
  <p th:text="*{name}"></p>
 </div>

<--中略-->
</HTML>
```
|ポイント|説明|備考|
|----|----|----|
|th:text="${変数}"|書式に注意(特に「""」の抜け)|変数名にキャメルケース可|
|th:object="${オブジェクト変数}"<br>th:text="*{オブジェクトのフィールド変数}"|「th:object」を使用すると、「*{変数名}」でオブジェクト変数を省略できる。||

<div>
【注意】この方法でフォームに値を入れると、Springがセッションに格納されるので、
同じページに返すと、フォームに値が入ったままになる。
フォームの値を空にする場合は、各要素に空文字をいれる。
</div>

```
if(hogeForm.getItemName() == null|hogeForm.getItemName() == "") {		
}else {
sampleMapper.inserthogeList(hogeForm);
//空文字を代入する。
hogeForm.setItemName("");
hogeForm.setRemark("");
}
```
## th:valueが空になる！?（ドハマり）
### inputボックスに初期値を入れたいが、value値が空になる。<br>(hWkListはコントローラークラスでaddAttribute済み)
```html
<tr>
 <td> 
  <!-- × th:fieldに値を入れているので、th:valueが空になる。-->
  <input type="number" th:value="{hWkList.intervalDay}" th:field="*{intervalDay}"/>
 </td>
</tr>
<tr>
 <td> 
  <!-- 〇 th:valueがにきちんと値が入り、初期値をセットできている。-->
  <input type="number" name="intervalDay" th:value="{hWkList.intervalDay}"/>
 </td>
</tr>
```

|☑ポイント|原因|
|----|----|
|th:valueが空になる|th:fieldやth:nameに値を入れているとvalueの中が空になる（後述）||
https://ti-tomo-knowledge.hatenablog.com/entry/2018/06/14/162616

## テキストにリンクを貼る
```html
<!--テキストタイプ-->
<a href="/shopping_list">買い物リスト</a>
<a th:href="@{/shopping_list}">買い物リスト</a>

<!--ボタンタイプ-->
<form method="get" action="/">
  <input type="submit" value="戻る">
</form>

<form method="get" th:action="@{/}">
  <input type="submit" value="戻る">
</form>
```
###  うまくいかないときの☑ポイント
|☑ポイント|解説||
|----|----|----|
|methodの「get」と「post」を間違っていないか||
|コントローラー側が「PostMapping」になっていないか|||
|コントローラー側でハンドラメソッドを定義し忘れていないか|ハンドラがないとHTMLの記述だけでは正しく動作しない。||
|リンクの記述を間違えていないか|th:で動的に定義する場合は、「"@{/}"というような記述が必要|

## ボタンを押して入力フォームをPOST

### ビュー側
```hoge.html
<div th:object="${inputHogeForm}">
  <form method="post" th:action="@{/inputHoge}">
    <input type="text" th:field="*{name}"/>
    <input type="submit" value="トップページ"/>
  </form>
</div>
```
### コントローラー側
```HogeController.java
@RequestMapping("/inputHoge")
public String getShoppingList(InputHogeForm inputHogeForm){

System.out.println(inputHogeForm.toString());

return "hoge";
}
```
### form側
```HogeForm.java
import lombok.Data;
@Data
public class HogeForm {
private String name;
}

```

### うまくいかないときの☑ポイント
|☑ポイント|解説|エラー例|
|----|----|----|
|「get」と「post」を間違っていないか||
|「th」がついているか|th:object<br>th:action<br>th:field<br>||
|「$」と「@」間違えていないか|"${inputHogeForm}"<br>"@{/inputHoge}"|org.attoparser.ParseException: Exception evaluating SpringEL expression: "/visit/regist" (template: "visit/regist" - line 69, col 35)<br>「evaluating 」：評価|
|「""」や「*」が抜けていないか|"${inputHogeForm}"<br>"@{/inputHoge}"<br>"*{name}"||
|"${変数}"の形をとっているか|"${変数}"とせず、直接変数を記述するとエラーになる|Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Expression "registVisitForm.companyName" is not valid: only variable expressions ${...} or selection expressions *{...} are allowed in Spring field bindings (template: "visit/regist" - line 76, col 56)|
|ビューを生成する時に、ModelAttribute に inputHogeFormがセットされていない|以下追加する<br>ModelAndView mv = new ModelAndView("hoge");<br>mv.addObject("inputHogeForm", new inputHogeForm());<br><br> https://teratail.com/questions/26218|Neither BindingResult nor plain target object for bean name 'inputHogeForm' available as request attribute|

## th:if= true or false

記述メモ

|用途|記述例|意味|
|----|----|----|
|「==」or 「eq」|th:if="\${a.name}==${b.name}"|a.name イコール b.name| 
|「!=」or 「neq」|th:if="\${a.name}!=${b.name}"|a.name ノットイコール b.name|
|null|th:if="${a.name}!=null"|a.nameがnullじゃないなら|



```sample.thtml
<div th:if="${a.name} neq ${b.name}" th:text="${'a<>bです。'}"></div>
```
```sample2.thtml
<div th:if="${a.name} == ${b.name}">

<!--trueなら実行-->
<!--falseなら実行されない-->

</div>
```

(参考)
https://itsakura.com/thymeleaf-if-loop

|タグ|改行|大別|備考|
|----|----|----|----|
|\<p>|あり|ブロック要素|Paragraphの略。段落を表す。文章の前後に余白ができる。|
|\<div>|あり|ブロック要素|divisionの略。|
|\<span>|なし|インライン要素|囲った範囲のスタイルを変更する等の使い方をする。|

## th:each属性の基本形
### コントローラー側
```:DemoContoroller.java
@GetMapping("/")
public String getMenu(Model model){	     
  List<String> demoList = new ArrayList<String>();
    demoList.add("赤");
    demoList.add("青");
    demoList.add("黄");
		 
  model.addAttribute("demoList", demoList);
   
   return "demo.html";	        
}
```
### ビュー側
```
<div th:each="demoList:${demoList}" th:text="${demoList}"></div>
```

|ポイント|解説|
|----|----|
|th:each="変数 : コレクション"|・変数名にはコレクションの実態が入ったクラスを指定する。例えば、EntitiyやFormクラス名を指定する。<br>・コントローラー側から渡した変数を使用すること<br>・entityクラスとGetter,Setterを作っておくこと|

コレクション内の要素が変数（繰り返し用変数）に入り、「\${変数名}」や「${変数名.プロパティー名}」で使用できる。
https://www.ne.jp/asahi/hishidama/home/tech/java/spring/boot/thymeleaf/th_each.html
### うまくいかないときの☑ポイント
|☑ポイント|解説||
|----|----|----|
|th:eachの構文が正しく記述できているか|th:each="変数名 : コレクション"||
|変数名にコレクションの実態を格納したクラス名をしていしているか|「demoList」|
|コントローラー側から渡した変数を使用しているか|「demoList」||
|コントローラー側で変数を正しく渡せているか|model.addAttribute("コントローラー側の変数", ビュー側の変数);|



## 選択肢 select
### テーブル内にth:eachを使用して動的に選択肢を生成し、formで送信する例
```
<form method="post" onsubmit="return cancelsubmit()" th:action="@{/housework_works_option}">
<table>
 <tr th:object="${houseworkList}">
  <td>
   <select th:field="*{categoryName}">
    <option th:each="houseworkList:${categoryList}"  
    th:object="${houseworkList}" th:text="*{categoryName}" 
    th:value="*{categoryName}">
   </select>
  </td>
 </tr>
</table>
<button type='submit' name='action'>追加</button>
</form>
```
|ポイント|解説|備考|
|----|----|----|
|value|フォームで送信する値を表す。この属性を省略すると、thymeleafが例外を投げる。|HTML属性|
|text|選択肢に表示される文字列をthymeleafで動的に生成する場合に使用する|thymeleafの独自属性<br>HTMLに「text」という属性はない。|

### うまくいかないときの☑ポイント
|☑ポイント|解説||
|----|----|----|
|th:fieldをselect内に設置したか。|設置しないと値がEntityやFormクラスの変数にセットされない||
|\<form>の位置が\<table>よりも内側に入っていないか|入っているとエラーになる。|
|\<option>の中に"value"がセットされているか。|Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Attribute "value" is required in "option" tags||

### うまくいかなかったので、javascriptとの併用に切り替えた例
#### やりたかったこと
* 特定のレコードのupdate
* 遷移先HTMLで、input textやselectにデフォルトで元のデータをセット
* 更新ボタンでupdate。その際にinput textやselectで書き換えた箇所は繁栄される

#### 発生した問題
* inputにデフォルトでデータを入れる場合、nameとvalueを使う必要があるが、th:field を使うとvalueが空になってしまう。
* コントローラーで値を受け取った際に挙動がうまくいかない。
→「houseworkList(categoryId=null,categoryName=houseworkList(categoryId=1,categoryName="hoge",・・・」のようにhouseworkLintが入れ子になり、入れ子の中に値が入ってしまう。

```
<!DOCTYPE html>
<head>
<title>家事詳細 編集</title>
</head>
<body>
<h1>家事詳細編集</h1>
<form method="post" onsubmit="return cancelsubmit()" th:action="@{/housework_works_edit_comp}">

<table border="1" >
<tr th:object="${houseworkList}">
    <td th:text="*{worksId}">カテゴリーID</td>
    <td>カテゴリー</td>
    <td>
        <select name="categoryName">
          <option th:each="houseworkList:${categoryList}"  th:object="${houseworkList}" th:text="*{categoryName}" th:value="*{categoryName}">
        </select>
    </td>
    <td>作業</td>
    <td>
      <input type="text" name="houseworkName" th:value="*{houseworkName}"/>
    </td>
</tr>
<tr th:object="${houseworkList}">
    <td>ここは隠す</td>
    <td>ステップ1</td>
    <td >     
      <span th:text="${hWkList.step1}"></span><br>
      <input type="text" th:field="*{step1}"/>
   </td>
</tr>
</table>
<input type="hidden" th:field="${houseworkList.worksId}" >
<button type='submit' name='action' value='add'>更新</button>
</form>
<br>
</body>
</html>
```
そこで、input textやselectにはjavascriptで値をセットすることにしたら、
上手くいった。


## th:textで動的に文字列を連結
### コントローラー側
```:TestController.java
@GetMapping("/test")
public String selectionTest(Model model) {
  //Colourインスタンス
  Colour colour = new Colour();
  colour.setName("黒");
  colour.setExample("カラス");

  model.addAttribute("colour",colour);
return"test.html";
	 }
```
###ビュー側
```:test.html
<p th:text="|${colour.example} は ${colour.name} です。|"></p>
```


### うまくいかないときの☑ポイント
|☑ポイント|解説||
|----|----|----|
|「｜」と「"」の順番は間違っていないか|「th:text=""」の中に「｜連結文字列｜」を入れる。||



## テキストボックスに薄いグレーのテキスト
```
<input type="text" value="" placeholder="表示させたいテキストを記述" >
```

## 日付のフォーマット変更方法
```
<td th:text="*{nexttimeHouoseworkDate)}">次回予定日</td>
↓↓
<td th:text="*{#dates.format(nexttimeHouoseworkDate,'yyyy年MM月dd日_HH:mm')}">次回予定日</td>
```
参考

https://qiita.com/tanibuchi12/items/a4dc726b88de8b6c473d

## JavaScript内でThymeleafを使用する際の注意点
* <b>使用するテンプレート内に記述すること</b>
* 外部ファイルには記述できない。（thymeleafを読み込まない）

```
例えば、setAttribute("th:action","@{/menu}"); 等しても 生成されるタグはそのまま表示されてしまう。
理想
<form method="get" action="/menu">
現実
<form method="get" th:action="@{/menu}">
```
thymeleafとjavascriptを組み合わせて動的にHTMLを作る場合は、「th:」部分の記述はテンプレートに
あらかじめ記述して起き、Javascriptで動的に生成したタグはappendChildで連結させると良い。

【記述例】
Aのボタンを押すと、Bが作られ、Cの処理でDの場所に設置、Eの処理で送信される。

```.html
<form id="submit-test" method="post" th:action="@{/cooking_menu}">
   <!--D-->
</form>
<form>
    <input id="submit-button" type="button"> <!--A-->
</form>
```
```.javascript
   document.getElementById("submit-button").onclick = function() {
        makeSendData()
   }
   function makeSendData() {
        // コントローラーに送るデータを作成する
        var parentTag = document.getElementById("ubmit-test");
        var sendElements = document.createElement("input");
        sendElements.setAttribute("type", "hidden");
        sendElements.setAttribute("name", "sample");
        sendElements.setAttribute("value", "test");
        //<input type="hidden" name="sample" value="test"> ・・・B
        parentTag.appendChild(sendElements); //...C
        //作成したデータをコントローラーに送る
        var fm = document.getElementById("submit-cooking-day");
        fm.submit(); //・・・E
    }
```


## 変数の使用
```
<script  type="text/javascript" th:inline="javascript">
/*<![CDATA[*/
 　<!--ここに記述-->
   変数名 = /*[[${変数}]]*/
/*]]>*/
</script>

```
### 記述例
```
<script  type="text/javascript" th:inline="javascript">
 /*<![CDATA[*/
  function sample(){
    menuList =/*[[${cookingList}]]*/
    menuStr = menuList[0].menuName
    console.log(menuStr);
  }
 /*]]>*/
</script>
```