forked from thymeleaf/thymeleaf.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsayhelloextendingthymeleaf5minutes.html
executable file
·296 lines (247 loc) · 11.8 KB
/
sayhelloextendingthymeleaf5minutes.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Say Hello! Extending Thymeleaf in 5 minutes - Thymeleaf: java XML/XHTML/HTML5 template engine</title>
<link rel="stylesheet" type="text/css" media="all" href="css/thymeleaf.css" />
<link rel="shortcut icon" href="http://www.thymeleaf.org/favicon.ico" />
<script type="text/javascript" src="https://apis.google.com/js/plusone.js">
{lang:'en', parsetags:'explicit'}
</script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-1276954-9']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<script type="text/javascript" src="sh/scripts/shCore.js"></script>
<script type="text/javascript" src="sh/scripts/shBrushXml.js"></script>
<script type="text/javascript" src="sh/scripts/shBrushJava.js"></script>
<link href="sh/styles/shCore.css" rel="stylesheet" type="text/css" />
<link href="sh/styles/shThemeThymeleaf.css" rel="stylesheet" type="text/css" />
</head>
<body lang="en" dir="ltr">
<div id="page">
<div id="menu">
<ul>
<li><a href="index.html" title="Home">Home</a></li>
<li><a href="features.html" title="Features">Features</a></li>
<li><a href="download.html" title="Download">Download</a></li>
<li><a href="documentation.html" title="Documentation">Documentation</a></li>
<li><a href="ecosystem.html" title="Ecosystem">Ecosystem</a></li>
<li><a href="http://forum.thymeleaf.org" title="User Forum">User Forum</a></li>
<li><a href="issuetracking.html" title="Issue Tracking">Issue Tracking</a></li>
</ul>
</div>
<div id="header">
<a href="index.html" title="Thymeleaf home"><img src="images/thymeleaflogonameverysmall.png" class="logo" alt="Thymeleaf Template Engine"/></a>
</div>
<div id="breadcrumb">
<a href="index.html">thymeleaf</a>
::
<a href="documentation.html">documentation</a>
::
articles
::
<span class="current">say hello! extending thymeleaf in 5 minutes</span>
</div>
<div id="content">
<h1>Say Hello! Extending Thymeleaf in 5 minutes</h1>
<p>
Extending Thymeleaf is easy: you only have to create a dialect and add it to your
template engine. Let's see how.
</p>
<p>
<i>(All the code seen here comes from a working application. You can download the source code
of such application from the <a href="documentation.html">documentation page</a>)</i>
</p>
<h2>Dialects</h2>
<p>
Thymeleaf Dialects are sets of features you can use in your templates. These features include:
</p>
<ul>
<li><b>Processing logic</b> specified via <i>processors</i> that apply on specific
DOM nodes.</li>
<li><b>DOCTYPE translations</b> which allow the automatic conversion of the template's
DOCTYPE declaration into a target one, thus allowing validation both before processing
--against a DTD including the dialect's tags and attributes-- and after processing
--against a DTD not including these, like e.g. a standard XHTML DTD.</li>
<li><b>DOCTYPE resolution entries</b> which are fed into Thymeleaf's template parsers and allow
the resolution of DTD files for the validation of templates (if required) in an offline manner,
this is, without the need to perform remote HTTP connections and/or download anything.</li>
</ul>
<p>
All of these features are optional, and a dialect can specify only some of them. For example, your
dialect might not need to specify any DOCTYPE translations because you do not plan to allow
the ability to validate templates against a DTD including your dialect's features.
</p>
<p>
If you've seen fragments of code written in the <i>Standard Dialects</i>, you should have noticed that
the processable attributes start with <kbd>th:</kbd>. That "<kbd>th</kbd>" is called the
<b>dialect prefix</b>, and it means that all tags and attributes processed by that dialect
will start with such prefix.
</p>
<p>
It is important also to note that <b>a Template Engine can be set several dialects at a time</b>,
thus allowing the processing of templates including features from all of the specified dialects
(think of dialects as a sort of <i>JSP taglibs</i> in steroids).
What's more, some of these dialects can <i>share prefix</i>, effectively acting as an aggregate
dialect.
</p>
<h2>The simplest dialect ever: Say Hello!</h2>
<p>
Let's create a dialect for one of our applications. This will be a Spring MVC application, so
we will be already using the SpringStandard dialect (have a look at the
<a href="documentation.html">Thymeleaf + Spring 3 tutorial</a> for more details). But we
want to add a new attribute that allows us to say hello to whoever we want, like this:
</p>
<script type="syntaxhighlighter" class="brush:html;gutter:false"><![CDATA[
<p hello:sayto="World">Hi ya!</p>
]]></script>
<h3>The processor</h3>
<p>
First, we will have to create the attribute processor that will take care of displaying
our salutation message.
</p>
<p>
All attribute processors implement the
<kbd>org.thymeleaf.processor.IProcessor</kbd> interface —which marks them as
<i>processors</i>— and specify a <i>matcher</i>
(<kbd>IProcessor.getMatcher()</kbd>) that implements the
<kbd>org.thymeleaf.processor.IAttributeNameProcessorMatcher</kbd> interface
—which specifically marks them as <i>attribute processors</i> and allows the
use of several engine optimizations—. However, several convenience
abstract implementations exist that not only implement the right interfaces, but also
ease the development of each type of processor.
</p>
<p>
Specifically, the <kbd>AbstractTextChildModifierAttrProcessor</kbd>
class specializes in attributes that substitute the body of the tag they sit on by
a text (which is exactly what we want). So we will extend this class for our attribute
processor:
</p>
<script type="syntaxhighlighter" class="brush:java"><![CDATA[
public class SayToAttrProcessor
extends AbstractTextChildModifierAttrProcessor {
public SayToAttrProcessor() {
// Only execute this processor for 'sayto' attributes.
super("sayto");
}
public int getPrecedence() {
// A value of 10000 is higher than any attribute in the
// SpringStandard dialect. So this attribute will execute
// after all other attributes from that dialect, if in the
// same tag.
return 10000;
}
//
// Our processor is a subclass of the convenience abstract implementation
// 'AbstractTextChildModifierAttrProcessor', which takes care of the
// DOM modifying stuff and allows us just to implement this 'getText(...)'
// method to compute the text to be set as tag body.
//
@Override
protected String getText(final Arguments arguments, final Element element,
final String attributeName) {
return "Hello, " + element.getAttributeValue(attributeName) + "!";
}
}
]]></script>
<h3>The dialect class</h3>
<p>
Creating our processor was very easy, but now we will need to create the <i>dialect class</i>,
which will be in charge of telling Thymeleaf that our processor is available.
</p>
<p>
We could make our dialect implement the <kbd>org.thymeleaf.dialect.IDialect</kbd> interface (which is the one
every dialect should implement), but instead we will extend a convenience abstract class
called <kbd>AbstractDialect</kbd> that
already implements all methods from that interface, returning empty values for all of them (so
that we only have to override the ones we require).
</p>
<script type="syntaxhighlighter" class="brush:java"><![CDATA[
public class HelloDialect extends AbstractDialect {
public HelloDialect() {
super();
}
//
// All of this dialect's attributes and/or tags
// will start with 'hello:'
//
public String getPrefix() {
return "hello";
}
//
// The processors.
//
@Override
public Set<IProcessor> getProcessors() {
final Set<IProcessor> processors = new HashSet<IProcessor>();
processors.add(new SayToAttrProcessor());
return processors;
}
}
]]></script>
<p>
As you can see, we didn't need to set any <i>DOCTYPE resolution entries</i> or <i>translations</i>.
We didn't need any of those because our dialect
is meant to be used together with the SpringStandard dialect, which already includes
a useful set of all those things (although we could have defined them here also if for some reason we wanted
to be more exhaustive). All our dialect does is specify a prefix (<kbd>hello</kbd>)
and our <i>"sayto"</i> attribute processor.
</p>
<h2>Using the hello dialect</h2>
<p>
Using our new dialect is very easy. This being a Spring MVC application, we just have to set
it at the <i>additionalDialects</i> property of the Template Engine bean, so that it is added to
the default <i>SpringStandard</i> dialect:
</p>
<script type="syntaxhighlighter" class="brush:html"><![CDATA[
<bean id="templateEngine"
class="org.thymeleaf.spring3.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
<property name="additionalDialects">
<set>
<bean class="thymeleafexamples.sayhello.dialect.HelloDialect"/>
</set>
</property>
</bean>
]]></script>
<p>
And our new attribute would work seamlessly:
</p>
<script type="syntaxhighlighter" class="brush:html;gutter:false"><![CDATA[
<p>Hello World!</p>
]]></script>
<h2>Want to know more?</h2>
<p>
Then have a look at <a href="sayhelloagainextendingthymeleafevenmore5minutes.html"><i>"Say
Hello Again! Extending Thymeleaf even more in another 5 minutes"</i></a>.
</p>
</div>
<div id="footer">
<div id="googleplus">
<div id="plusone-div" class="plusone"></div>
<script type="text/javascript">
gapi.plusone.render('plusone-div',{"size": "small", "count": "true", "href": "http://www.thymeleaf.org"});
</script>
</div>
<div id="twitter">
<a href="http://twitter.com/thymeleaf" class="twitter-follow-button" data-show-count="false">Follow @thymeleaf</a>
<script src="http://platform.twitter.com/widgets.js" type="text/javascript"></script>
</div>
<div id="copy">
Copyright © The <a href="team.html">THYMELEAF Team</a>. See <a href="documentation.html">applicable licenses</a>.
</div>
</div>
</div>
<script type="text/javascript">
SyntaxHighlighter.all();
</script>
</body>
</html>