레이아웃 만들기
발번역입니당ㅠㅠ 개선하겠습니다!
템플릿 조각 정의
템플릿을 작성할 때, 레이아웃을 이루는 fragment는 th:fragment="조각이름"으로 선언합니다.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</div>
</body>
</html>
레이아웃에 th:insert="~{templatename :: fragmentname}"으로 삽입할 수 있습니다.
fragment의 추가는 th:fragment, th:replace 둘 다 가능합니다.
"~{templatename :: fragmentname}" == "templatename :: fragmentname"
<body>
...
<div th:insert="~{footer :: copy}"></div>
</body>
아래와 같이 Th:fragment 속성 없이 id 값만으로도 호출할 수 있습니다.
...
<div id="copy-section">
© 2011 The Good Thymes Virtual Grocery
</div>
...
<body>
...
<div th:insert="~{footer :: #copy-section}"></div>
</body>
replace와 insert의 차이!
- th:insert : 레이아웃에서 호출하고 있는 태그의 body로 fragment(tag 포함)를 삽입합니다.
- th:replace : 레이아웃에서 호출하고 있는 태그까지 fragment로 대체합니다.
<footer th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</footer>
<body>
...
<div th:insert="footer :: copy"></div>
<div th:replace="footer :: copy"></div>
</body>
<body>
...
<div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
</div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
</body>
fragment에서 변수로 값 표현하기
<div th:fragment="frag (onevar,twovar)">
<p th:text="${onevar} + ' - ' + ${twovar}">...</p>
</div>
fragment는 아래 두가지 방법으로 파라미터를 받아서 활용할 수 있습니다.
<div th:replace="::frag (${value1},${value2})">...</div>
<div th:replace="::frag (onevar=${value1},twovar=${value2})">...</div>
2번째 문법에서 파라미터 순서는 중요하지 않습니다.
<div th:replace="::frag (twovar=${value2},onevar=${value1})">...</div>
인자 없는 fragment에서 변수 사용하기
fragment를 선언할 때, 파라미터를 선언하지 않아도 변수를 사용할 수 있습니다.
<div th:fragment="frag">
...
</div>
위의 2번째 문법으로만 파라미터 선언없이 변수 값을 사용할 수 있습니다.
<div th:replace="::frag (onevar=${value1},twovar=${value2})">
이러한 방식은 th:replace
와 th:with
을 사용한 것과 같습니다.
<div th:replace="::frag" th:with="onevar=${value1},twovar=${value2}">
유연하게 Fragment 사용하기 : Mark-up 태그 재활용
조각 표현식 덕분에 fragment를 지정할 수 있습니다.
이로써 매우 유연한 템플릿 레이아웃 메커니즘이 생성 됩니다.
아래 예제에서 title
및 links
변수를 사용합니다.
<head th:fragment="common_header(title,links)">
<title th:replace="${title}">The awesome application</title>
<!-- Common styles and scripts -->
<link rel="stylesheet" type="text/css" media="all" th:href="@{/css/awesomeapp.css}">
<link rel="shortcut icon" th:href="@{/images/favicon.ico}">
<script type="text/javascript" th:src="@{/sh/scripts/codebase.js}"></script>
<!--/* Per-page placeholder for additional links */-->
<th:block th:replace="${links}" />
</head>
위 fragment의 title과 links는 부분은 아래의 title, link태그들로 대체됩니다.
...
<head th:replace="base :: common_header(~{::title},~{::link})">
<title>Awesome - Main</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
<link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">
</head>
...
아래에 보이는 코드처럼, 템플릿의 <title>
및 <link>
태그를 title
및 links
변수의 값으로 사용됩니다.
...
<head>
<title>Awesome - Main</title>
<!-- Common styles and scripts -->
<link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
<link rel="shortcut icon" href="/awe/images/favicon.ico">
<script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>
<link rel="stylesheet" href="/awe/css/bootstrap.min.css">
<link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css">
</head>
...
빈 Fragment 사용
특별한 표현식 인 빈 조각 ( ~{}
)은 대체할 태그를 지정하지 않는 데 사용할 수 있습니다 .
<head th:replace="base :: common_header(~{::title},~{})">
<title>Awesome - Main</title>
</head>
...
Fragment ( links
) 의 두 번째 매개 변수가 빈 프래그먼트로 설정되어 <th:block th:replace="${links}" />
블록에는 아무 것도 대체되지 않습니다.
...
<head>
<title>Awesome - Main</title>
<!-- Common styles and scripts -->
<link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
<link rel="shortcut icon" href="/awe/images/favicon.ico">
<script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>
</head>
...
작동하지 않는 토큰 사용
fragment가 현재 태그를 기본값으로 사용하도록하려면 no-op을 프래그먼트의 매개 변수로 사용할 수도 있습니다.
...
<head th:replace="base :: common_header(_,~{::link})">
<title>Awesome - Main</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
<link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">
</head>
...
title
에 no-op이 적용되어 title 값은 대체되지 않습니다.
<title th:replace="${title}">The awesome application</title>
따라서 결과는 다음과 같습니다.
...
<head>
<title>The awesome application</title>
<!-- Common styles and scripts -->
<link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
<link rel="shortcut icon" href="/awe/images/favicon.ico">
<script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>
<link rel="stylesheet" href="/awe/css/bootstrap.min.css">
<link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css">
</head>
...
Fragment 조건부 삽입
위의 표현식 덕분에 우리는 매우 간단하고 우아한 방법으로 fragment의 조건 삽입을 수행할 수 있습니다.
예를 들어, 사용자가 관리자인 경우에만common :: adminhead
를 삽입하고 그렇지 않은 경우 아무것도 삽입하지 않기 위해 ~{}
표현을 사용할 수 있습니다.
...
<div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : ~{}">...</div>
...
또한 지정된 조건이 충족되는 경우에만 fragment를 삽입하기 위해 no-operation 토큰을 사용할 수 있지만 조건이 충족되지 않으면 태그를 수정하지 않고 그대로 둘 수 있습니다.
...
<div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : _">
Welcome [[${user.name}]], click <a th:href="@{/support}">here</a> for help-desk support.
</div>
...
또한, 플래그를 사용하여 템플릿 리소스가 있는지 확인하도록 template resolver를 구성한 경우 기본작업 checkExistence
의 조건으로 fragment를 사용할 수 있습니다 .
...
<!-- The body of the <div> will be used if the "common :: salutation" fragment -->
<!-- does not exist (or is empty). -->
<div th:insert="~{common :: salutation} ?: _">
Welcome [[${user.name}]], click <a th:href="@{/support}">here</a> for help-desk support.
</div>
...
프로토타이핑에서 유용한 th:remove
th:remove
는 값에 따라 5가지 방식으로 작동할 수 있습니다.
all
: 선언된 태그와 모든 자식을 제거합니다.body
: 하위 태그만 제거합니다.tag
: 선언된 태그만 제거합니다.all-but-first
: 첫 번째 태그를 제외하고 포함하는 태그의 모든 하위를 제거합니다.none
: 어떤 태그도 제거하지 않습니다.
<table>
<thead>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
</thead>
<tbody th:remove="all-but-first">
<tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
<td>
<span th:text="${#lists.size(prod.comments)}">2</span>
comment/s
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>
</td>
</tr>
<tr class="odd">
<td>Blue Lettuce</td>
<td>9.55</td>
<td>no</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr>
<td>Mild Cinnamon</td>
<td>1.99</td>
<td>yes</td>
<td>
<span>3</span> comment/s
<a href="comments.html">view</a>
</td>
</tr>
</tbody>
</table>
레이아웃 상속
단일 HTML파일을 레이아웃으로 이용하기 위해서, fragment가 사용될 수 있습니다.
<!DOCTYPE html>
<html th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org">
<head>
<title th:replace="${title}">Layout Title</title>
</head>
<body>
<h1>Layout H1</h1>
<div th:replace="${content}">
<p>Layout content</p>
</div>
<footer>
Layout footer
</footer>
</body>
</html>
위의 예제는 title
과 content
를 파라미터로 포함하는 fragment를 레이아웃으로 선언하고 있습니다.
두 파라미터 태그 모두 상속하는 페이지에서 fragment 표현식에 의해서 대체될 것입니다.
<!DOCTYPE html>
<html th:replace="~{layoutFile :: layout(~{::title}, ~{::section})}">
<head>
<title>Page Title</title>
</head>
<body>
<section>
<p>Page content</p>
<div>Included on page</div>
</section>
</body>
</html>
위의 예제에서 HTML 태그는 레이아웃 fragment로 대체될 것입니다. 반면, 레이아웃에서 title
, content
는 title과 section 태그로 각각 대체될 것입니다.