타임리프에서 반복은 th:each를 사용한다. 추가로 반복에서 사용할 수 있는 여러 상태 값을 지원한다.
컨트롤러
@GetMapping("/each")
public String each(Model model) {
addUsers(model);
return "basic/each";
}
private void addUsers(Model model) {
List<User> list = new ArrayList<>();
list.add(new User("userA", 10));
list.add(new User("userB", 20));
list.add(new User("userC", 30));
model.addAttribute("users", list);
}
템플릿 코드
<tr th:each="user : ${users}">
<td th:text="${user.username}">username</td>
<td th:text="${user.age}">0</td>
</tr>
<tr th:each="user, userStat : ${users}">
<td th:text="${userStat.count}">username</td>
<td th:text="${user.username}">username</td>
<td th:text="${user.age}">0</td>
이하 생략
동작 설명: 컨트롤러에서 User 클래스 인스턴스들을 담은 list를 model에 users란 명칭으로 추가하고 템플릿에 넘겨주면,
1.tr(테이블의 행)에 th:each는 users 리스트를 반복(iteration)하면서 각 요소를 user 변수에 할당한다.
2.td(각 셀)에 프로퍼티 접근 으로 데이터를 할당한다.
자료구조(컬렉션)은 List 뿐만 아니라 배열, java.util.Iterable, java.util.Enumeration 을 구현한 모든 객체를 반복에 사용할 수 있다. Map도 사용할 수 있는데 이 경우 변수에 담기는 값은 Map.Entry 이다.
th:each="변수명, 상태변수명 ": ${model이름} 형식으로 작성하며, 첫번째는 컬렉션에 대한 인스턴스를 하나씩 가져오고, 두번째는 현재 루프에 대한 상태를 나타낸다.
반복 상태 속성(th:each 두번째 인자)
타임리프의 조건식:if , unless ( if 의 반대)
th:if=${해당 위치에 문자열에 대한 논리 연산이 충족해야 태그가 렌더링된다.}
컨트롤러
@GetMapping("/condition")
public String condition(Model model) {
addUsers(model);
return "basic/condition";
}
템플릿 코드
<span th:text="${user.age}">0</span>
<span th:text="'미성년자'" th:if="${user.age lt 20}"></span>
<span th:text="'미성년자'" th:unless="${user.age ge 20}"></span>
HTML 원본 페이지
해당 인스턴스의 age가 span "'미성년자'" 조건에 맞지 않아 span 태그가 렌더링되지 않았다.
1.HTML 주석
<!--<span th:text="${data}">html data</span>-->
2.타임리프 파서 주석
<!--/* [[${data}]] */-->
3.타임리프 프로토타입 주석
<!--/*/<span th:text="${data}">html data</span>/*/-->
타임 리프는 HTML 주석 안에 타임리프 문법이 있더라도 렌더링 하지 않는다. 타임 리프 프로토 타입 주석은 파일 자체를 웹 브라우져에서 렌더링하지 않지만 웹 어플리케이션 실행시 타임 리프가 구동이 되며 렌더링된다.
템플릿 코드
<span th:text="${data}">html data</span>
<h1>1. 표준 HTML 주석</h1>
<!--<span th:text="${data}">html data</span>-->
<h1>2. 타임리프 파서 주석</h1>
<!--/* [[${data}]] */-->
<!--/*-->
<span th:text="${data}">html data</span>
<!--*/-->
<h1>3. 타임리프 프로토타입 주석</h1>
<!--/*/
<span th:text="${data}">html data</span>
/*/-->
HTML 원본 페이지
th:block는 HTML에 직접 영향을 미치지 않으면서 타임리프에서 반복문이나 조건문을 사용해 여러 요소를 그룹으로 처리할 수 있는 타임리프 유일한 태그 이다.
템플릿 코드
<th:block th:each="user : ${users}">
<div>
사용자 이름1 <span th:text="${user.username}"></span>
사용자 나이1 <span th:text="${user.age}"></span>
</div>
<div>
요약 <span th:text="${user.username} + ' / ' + ${user.age}"></span>
</div>
</th:block>
단순 span으로 반복문을 처리한다면 HTML에 불필요한 태그가 렌더링되어 HTML 구조에 영향을 줄수 있다. 특히 블록(th:block)은 타임리프의 조건문(th:if), 반복문(th:each) 등과 조합에 유용하다.
HTML 원본 페이지
<th:block th:each> 문이 사라진것을 볼수 있다.
자바스크립트에서 타임리프를 편리하게 사용할수 있는 인라인 기능을 타임리프가 제공한다.
<script th:inline="javascript">
템플릿 코드
<!-- 자바스크립트 인라인 사용 전 -->
<script>
var username = [[${user.username}]];
var age = [[${user.age}]];
//자바스크립트 내추럴 템플릿
var username2 = /*[[${user.username}]]*/ "test username";
//객체
var user = [[${user}]];
</script>
<!-- 자바스크립트 인라인 사용 후 -->
<script th:inline="javascript">
var username = [[${user.username}]];
var age = [[${user.age}]];
//자바스크립트 내추럴 템플릿
var username2 = /*[[${user.username}]]*/ "test username";
//객체
var user = [[${user}]];
</script>
HTML 원본 페이지
여기서 자바스크립트 인라인을 사용하지 않는다면 문법이 깨져 오류가 발생한다. 이와같이 자바스크립트에 타임리프 값들을 전달하기 쉽지 않다.
또한 Java 객체를 javascript 변수에 할당한다면 to_string 함수가 호출되어 var user = BasicController.User(username=userA, age=10); 이렇게 그대로 들어가버린다.
따라서 th:inline="javascript"를 사용한다면 이 영역은 자바스크립트 영역이고, 타임리프가 다음과 같은 편의 기능을 제공한다.
자동 이스케이프: 큰따옴표 ("): \" ,작은따옴표 ('): \' , 백슬래시 (): \ ,줄바꿈 (\n) 및 캐리지 리턴 (\r): \n, \r
또한 javascripts 인라인은 each를 지원한다.
<!-- 자바스크립트 인라인 each -->
<script th:inline="javascript">
[# th:each="user, stat : ${users}"]
var user[[${stat.count}]] = [[${user}]];
[/]
</script>
결과
<script>
var user1 = {"username":"userA","age":10};
var user2 = {"username":"userB","age":20};
var user3 = {"username":"userC","age":30};
</script>
Thymeleaf -4에 계속 ..