[JAVA] Thymeleaf th: field meaning and Spring Framework value passing mechanism

Introduction

In Thymeleaf, I used to use th: field a lot just like remembering one of the idiots, but I feel that I have recently escaped from that level of understanding, so I would like to summarize the contents.

As an overview, the focus will be on the Form class, which is responsible for passing values in the Spring Framework. Ultimately, the value is passed without using the Form class at all.

The aim is as follows.

It's a story of how to understand, so ** it's not very practical. ** **

Premise

You need a web project created with the Spring Framework. The template engine is based on Thymeleaf.

General implementation

First, let's consider a general implementation using Form. From here you can tweak the code and eventually delete the Form class.

BookForm.java

public class BookForm {

	private String title;
	private int price;
	private String summary;
	//	Getter,Setter omitted
}

SampleController.java

@Controller
public class SampleController {

	//input screen
	@GetMapping("/input")
	public String input(@ModelAttribute("bookForm") BookForm bookForm) {
		return "input";
	}

	//Book information screen
	@PostMapping("/bookinfo")
	public String toBookInfo(@ModelAttribute("bookForm") BookForm bookForm) {
		return "bookinfo";
	}
}

input.html

	<!--body tab inside only-->
	<h3>Implementation using Form class</h3>
	<form th:action="@{/bookinfo}" method="post" th:object="${bookForm}">
	<label>title</label><br>
	<input type="text" th:field="*{title}"><br>
	<label>price</label><br>
	<input type="text" th:field="*{price}"><br>
	<label>Overview</label><br>
	<input type="text" th:field="*{summary}"><br>
	<button>Send</button>
	</form>

bookinfo.html

	<!--body tab inside only-->
	<h3>Implementation using Form class</h3>
	<div th:object="${bookForm}">
	<label>title</label><br>
	<span th:text="*{title}"></span><br>
	<label>price</label><br>
	<span th:text="*{price}"></span><br>
	<label>Note</label><br>
	<span th:text="*{summary}"></span><br>
	</div>

The input value is output, which is a very simple implementation.

スクリーンショット 2018-11-06 22.23.08.png スクリーンショット 2018-11-06 22.28.54.png

Delete th: field and th: object in input.html

First, consider removing th: field and th: object from input.html. Now, in order to know the identity of th: field and th: object, let's check what the input.html written in Thymeleaf is by using the developer tools of Chrome. Let's do it.

スクリーンショット 2018-11-06 22.49.40.png

th: object disappears from the form tag, and the text box for entering the title described in th: field becomes <input type =" text "id =" title "name =" title "value> You can see that there is. Simply put, you can erase th: field and th: object by writing exactly as it appears in the developer tools. If you write only the necessary description, it will be as follows.

input.html

	<!--body tab inside only-->
	<h3>th:Implementation with object deleted</h3>
	<form th:action="@{/bookinfo}" method="post">
	<label>title</label><br>
	<input type="text" name="title"><br>
	<label>price</label><br>
	<input type="text" name="price"><br>
	<label>Overview</label><br>
	<input type="text" name="summary"><br>
	<button>Send</button>
	</form>

Even with this html description, the "input value is output" process shown in [General implementation](#General implementation) can be realized.

The fact that the description of th: object is gone means that the Form class of the input method of SampleController.java can also be deleted.

	//input screen
	@GetMapping("/input")
	public String input() {
		return "input";
	}

The most important thing is ** If you specify the field name of Form class in the neme attribute of the input tag, the entered value will be stored in that field ** about it.

Almost everything I want to say in this article lies in this fact. (The above is not exactly the correct explanation for the behavior of th: field. For details, see Form binding function by th: field and th: object (input tag / basic input system) I would appreciate it if you could refer to: //casual-tech-note.hatenablog.com/entry/2018/10/10/224250)

Swap Form classes

With the above mechanism in mind, you can do this: For example, suppose you have the following Form class called MovieForm, which is different from BookForm.

MovieForm.java

public class MovieForm {

	private String title;
	private String summary;
	//	Getter,Setter omitted
}

(Sorry for the unnatural example) Suppose you want to treat the book information that was made into a movie using this MovieForm class as movie information. In bookinfo.html, prepare a "Send as movie information" button and rewrite it so that BookForm is sent to the last.

bookinfo.html (significantly changed)

	<!--body tab inside only-->
	<h3>Swap values from BookForm to MoiveForm</h3>
	<form th:action="@{/movieinfo}" method="post" th:object="${bookForm}">
	<label>title</label><br>
	<input type="text" readonly th:field="*{title}"><br>
	<label>price</label><br>
	<input type="text" readonly th:field="*{price}"><br>
	<label>Overview</label><br>
	<input type="text" readonly name="summary" th:value="*{summary}"><br>
	<button>Also sent as movie information</button>
	</form>

SampleController.java (additional note)

	//Movie information output screen (additional information)
	@PostMapping("/movieinfo")
	public String toMovieinfo(@ModelAttribute("movieForm") MovieForm movieForm) {
		return "movieinfo";
	}

movieinfo.html

	<!--body tab inside only-->
	<h3>Swap values from BookForm to MoiveForm</h3>
	<div th:object="${movieForm}">
	<label>title</label><br>
	<span th:text="*{title}"></span><br>
	<label>Overview</label><br>
	<span th:text="*{summary}"></span><br>
	</div>

The processing result is as follows. スクリーンショット 2018-11-11 19.18.16.png スクリーンショット 2018-11-11 19.18.31.png

The value sent as the BookForm class in this way is stored in MovieForm and output in movieinfo.html. It's a slightly twisted implementation, so some people may have a strange feeling.

The point is that BookForm and MovieForm have fields with the same name such as title and summary. I wrote two types in bookinfo.html, but in this processing result

<input type="text" readonly th:field="*{title}"> Is <input type =" text "readonly name =" title "value =" AIUEO ">

<input type="text" readonly name="summary" th:value="*{summary}"> Is <input type =" text "readonly name =" summary "value =" test ">

Has the same meaning as. In other words, the value of the ** name attribute is the same as the field name of Form **, so there is a tsuji 褄.

Specify a value for button and receive it in Form class

** Well, can it be implemented without using th: field? ** ** Isn't it about to come to mind?

Now, let's consider receiving the value specified for button. Add a category field like the following to MovieForm to add buttons for each category in bookinfo.html. The text will be displayed in movieinfo.html depending on the button you press.

MovieForm.java

public class MovieForm {

	private String title;
	private String summary;
	//Added category field
	private String category;
	//	Getter,Setter omitted
}

bookinfo.html (Add multiple buttons with values in category)

	<!--body tab inside only-->
	<h3>Specify a value for button and receive it in Form class</h3>
	<form th:action="@{/movieinfo}" method="post" th:object="${bookForm}">
	<label>title</label><br>
	<input type="text" readonly th:field="*{title}"><br>
	<label>price</label><br>
	<input type="text" readonly th:field="*{price}"><br>
	<label>Overview</label><br>
	<input type="text" readonly name="summary" th:value="*{summary}"><br>
	<button name="category" value="action">action映画として送信</button>
	<button name="category" value="Fantasy">Fantasy映画として送信</button>
	<button name="category" value="SF">Send as science fiction movie</button>
	</form>

movieinfo.html

	<!--body tab inside only-->
	<h3>Specify a value for button and receive it in Form class</h3>
	<div th:object="${movieForm}">
	<label>title</label><br>
	<span th:text="*{title}"></span><br>
	<label>Overview</label><br>
	<span th:text="*{summary}"></span><br>
The category is<span th:text="*{category}"></span>is.
	</div>

The result is as follows

If you use only th: field, it is difficult to change the value value of the item that can not be entered, but when you know that ** name value and value value are linked **, like this It is also possible to change the processing depending on the value value of the button.

Delete Form class

As a final step, we will erase the Form class. The annotation @ RequestParam plays an active role here.

The implementation that outputs the input value is as follows.

input.html → See [Erase th: field and th: object in input.html](Delete th field and th object in #inputhtml)

SampleController.java

@Controller
public class SampleController {

	//input screen
	@GetMapping("/input")
	public String input() {
		return "input";
	}

	//Book information output screen
	@PostMapping("/bookinfo")
	public String toBookInfo(@RequestParam Map<String, String> bookmap, Model model) {
		model.addAttribute("bookmap", bookmap);
		return "bookinfo";
	}
}

bookinfo.html

	<!--body tab inside only-->
	<h3>Delete Form class</h3>
	<label>title</label><br>
	<span th:text="${bookmap.title}"></span><br>
	<label>price</label><br>
	<span th:text="${bookmap.price}"></span><br>
	<label>Note</label><br>
	<span th:text="${bookmap.summary}"></span><br>

Now you can get the same result as General Implementation (#General Implementation) without using any Form class. When the value entered in input.html with @ RequestParam is received in Map, the value is stored in bookinfo in the form of {title = AIUEO, price = 1000, summary = test}. You can see well that the ** name value and the value value ** that I mentioned many times in this article are sent in association with each other.

Also, the advantage of this implementation is that you can change the input value.

Trigger and supplement

In the first place, I thought "I want to erase" th: field`` "because I had to create a variable input screen.

As a result of various conclusions, I think that if you can use th: field, you should actively use it, and you should not use up to @ RequestParam to delete the Form class.

In the implementation where the input value is received by Map using @ RequestParam, Handling of values when there is a back button Error handling (Spring Framework annotations cannot be used, of course) And so on.

In this example as well, it may be enough to specify a value for button and receive it, but I think that readability will be significantly reduced if you do [Swap Form class](Swap #Form class). I will.

Summary

** th: field means name and value (and id) attributes in HTML ** ** The value of the value attribute is sent in association with the value specified in the name attribute, and the Controller side receives it ** that's all!

reference

Spring MVC populate @RequestParam Map<String, String> Spring MVC controller arguments

As you can see in the comments, this article has more details on the behavior of th: field. Form binding function by th: field and th: object (input tag / basic input system)

Recommended Posts

Thymeleaf th: field meaning and Spring Framework value passing mechanism
Play Framework studying value passing, form helper