- Contexts 2 4 – Fast Window Switcher System
- Contexts 2 4 – Fast Window Switcher Kit
- Contexts 2 4 – Fast Window Switcher Switch
- Windows Switcher App
- Contexts 2 4 – Fast Window Switcher Wiring
Contexts 3 6 1 – Fast Window Switcher Installation 3.0 Front-end Services Having stated the assumption we made in our thread framework, we now focus our attention on the services provided by the front-end runtime system. 3.1 Operating System Support In order to perform a context switch, we need the operating system to provide support that. Multiple desktops are great for keeping unrelated, ongoing projects organized, or for quickly switching desktops before a meeting. To create multiple desktops: On the taskbar, select Task view New desktop. Open the apps you want to use on that desktop. To switch between desktops, select Task view again.
With Microsoft's recent firmware update for the Surface Pro 3, among the new bits and pieces was a new Marvell Wifi driver that lets you switch between 2.4GHz and 5GHz bands on the fly. For those of us using a dual-band router, the updated driver should help resolve any related issues.
Switching bands is easy, so long as you know where to look. Here's how to do it.
- Using the universal search on the Start Screen, search for 'Device Manager.'
- Open Device Manager
- Find 'Network Adapters' in the list and expand it
- Tap and hold or right click on 'Marvell AVASTAR Wireless-AC Network Controller' and open its properties
- Along the top of the dialog box, open the Driver tab
- Verify the version number is 15.68.3073.151 before continuing
- Assuming all is correct thus far, hit the Advanced tab. This is where you'll change bands.
- The dropdown 'Value' box on the right will have options for 2.4GHz, 5GHz and Auto when the Property box on the left has 'Band' highlighted.
- Select your desired band, hit OK, and you're good to go!
One thing worth noting is that should you specify only 2.4GHz or 5GHz, your Surface will only connect on those bands. If you then leave the house and try to connect to Wifi elsewhere, it's worth making sure you've set it back to Auto to keep things running nice and smoothly.
For more Surface Pro 3 tips and tricks be sure to swing by our dedicated Surface Pro 3 page here
Thanks jlangner for the heads up!
Fabled learningsXbox laments closing Fable dev Lionhead, describing it as a 'mistake'
Fable developer Lionhead Studios closed down in 2016, after it became apparent Fable Legends wasn't going to work for the franchise. Microsoft now views the whole running of Lionhead as a mistake, and has vowed to learn from it.
1.1 What is Thymeleaf?
Thymeleaf is a Java library. It is an XML/XHTML/HTML5 template engine able to apply a set of transformations to template files in order to display data and/or text produced by your applications.
It is better suited for serving XHTML/HTML5 in web applications, but it can process any XML file, be it in web or in standalone applications.
The main goal of Thymeleaf is to provide an elegant and well-formed way of creating templates. In order to achieve this, it is based on XML tags and attributes that define the execution of predefined logic on the DOM (Document Object Model), instead of explicitly writing that logic as code inside the template.
Its architecture allows a fast processing of templates, relying on intelligent caching of parsed files in order to use the least possible amount of I/O operations during execution.
And last but not least, Thymeleaf has been designed from the beginning with XML and Web standards in mind, allowing you to create fully validating templates if that is a need for you.
1.2 What kind of templates can Thymeleaf process?
Out-of-the-box, Thymeleaf allows you to process six kinds of templates, each of which is called a Template Mode:
- XML
- Valid XML
- XHTML
- Valid XHTML
- HTML5
- Legacy HTML5
All of these modes refer to well-formed XML files except the Legacy HTML5 mode, which allows you to process HTML5 files with features such as standalone (not closed) tags, tag attributes without a value or not written between quotes. In order to process files in this specific mode, Thymeleaf will first perform a transformation that will convert your files to well-formed XML files which are still perfectly valid HTML5 (and are in fact the recommended way to create HTML5 code)1.
Also note that validation is only available for XML and XHTML templates.
Nevertheless, these are not the only types of template that Thymeleaf can process, and the user is always able to define his/her own mode by specifying both a way to parse templates in this mode and a way to write the results. This way, anything that can be modelled as a DOM tree (be it XML or not) could effectively be processed as a template by Thymeleaf.
1.3 Dialects: The Standard Dialect
Thymeleaf is an extremely extensible template engine (in fact it should be better called a template engine framework) that allows you to completely define the DOM nodes that will be processed in your templates and also how they will be processed.
An object that applies some logic to a DOM node is called a processor, and a set of these processors —plus some extra artifacts— is called a dialect, of which Thymeleaf's core library provides one out-of-the-box called the Standard Dialect, which should be enough for the needs of a big percent of users.
The Standard Dialect is the dialect this tutorial covers. Every attribute and syntax feature you will learn about in the following pages is defined by this dialect, even if that isn't explicitly mentioned.
Of course, users may create their own dialects (even extending the Standard one) if they want to define their own processing logic while taking advantage of the library's advanced features. A Template Engine can be configured several dialects at a time.
The official thymeleaf-spring3 and thymeleaf-spring4 integration packages both define a dialect called the 'SpringStandard Dialect', mostly equivalent to the Standard Dialect but with small adaptations to make better use of some features in Spring Framework (for example, by using Spring Expression Language instead of Thymeleaf's standard OGNL). So if you are a Spring MVC user you are not wasting your time, as almost everything you learn here will be of use in your Spring applications.
The Thymeleaf Standard Dialect can process templates in any mode, but is especially suited for web-oriented template modes (XHTML and HTML5 ones). Besides HTML5, it specifically supports and validates the following XHTML specifications: XHTML 1.0 Transitional, XHTML 1.0 Strict, XHTML 1.0 Frameset, and XHTML 1.1.
Most of the processors of the Standard Dialect are attribute processors. This allows browsers to correctly display XHTML/HTML5 template files even before being processed, because they will simply ignore the additional attributes. For example, while a JSP using tag libraries could include a fragment of code not directly displayable by a browser like:
…the Thymeleaf Standard Dialect would allow us to achieve the same functionality with:
Which not only will be correctly displayed by browsers, but also allow us to (optionally) specify a value attribute in it ('James Carrot', in this case) that will be displayed when the prototype is statically opened in a browser, and that will be substituted by the value resulting from the evaluation of ${user.name}
during Thymeleaf processing of the template.
If needed, this will allow your designer and developer to work on the very same template file and reduce the effort required to transform a static prototype into a working template file. The ability to do this is a feature usually called Natural Templating.
1.4 Overall Architecture
Thymeleaf's core is a DOM processing engine. Specifically, it uses its own high-performance DOM implementation —not the standard DOM API— for building in-memory tree representations of your templates, on which it later operates by traversing their nodes and executing processors on them that modify the DOM according to the current configuration and the set of data that is passed to the template for its representation —known as the context.
The use of a DOM template representation makes it very well suited for web applications because web documents are very often represented as object trees (in fact DOM trees are the way browsers represent web pages in memory). Also, building on the idea that most web applications use only a few dozen templates, that these are not big files and that they don't normally change while the application is running, Thymeleaf's usage of an in-memory cache of parsed template DOM trees allows it to be fast in production environments, because very little I/O is needed (if any) for most template processing operations.
If you want more detail, later in this tutorial there is an entire chapter dedicated to caching and to the way Thymeleaf optimizes memory and resource usage for faster operation.
Nevertheless, there is a restriction: this architecture also requires the use of bigger amounts of memory space for each template execution than other template parsing/processing approaches, which means that you should not use the library for creating big data XML documents (as opposed to web documents). As a general rule of thumb (and always depending on the memory size of your JVM), if you are generating XML files with sizes around the tens of megabytes in a single template execution, you probably should not be using Thymeleaf.
The reason we consider this restriction only applies to data XML files and not web XHTML/HTML5 is that you should never generate web documents so big that your users' browsers set ablaze and/or explode – remember that these browsers will also have to create DOM trees for your pages!
1.5 Before going any further, you should read…
Thymeleaf is especially suited for working in web applications. And web applications are based on a series of standards that everyone should know very well but few do – even if they have been working with them for years.
With the advent of HTML5, the state of the art in web standards today is more confusing than ever… are we going back from XHTML to HTML? Will we abandon XML syntax? Why is nobody talking about XHTML 2.0 anymore?
So before going any further in this tutorial, you are strongly advised to read an article on Thymeleaf's web site called 'From HTML to HTML (via HTML)', which you can find at this address: http://www.thymeleaf.org/doc/articles/fromhtmltohtmlviahtml.html
The source code for the examples shown in this and future chapters of this guide can be found in the Good Thymes Virtual Grocery GitHub repository.
2.1 A website for a grocery
In order to better explain the concepts involved in processing templates with Thymeleaf, this tutorial will use a demo application you can download from the project web site.
This application represents the web site of an imaginary virtual grocery, and will provide us with the adequate scenarios to exemplify diverse Thymeleaf features.
We will need a quite simple set of model entities for our application: Products
which are sold to Customers
by creating Orders
. We will also be managing Comments
about those Products
:
Our small application will also have a very simple service layer, composed by Service
objects containing methods like:
Finally, at the web layer our application will have a filter that will delegate execution to Thymeleaf-enabled commands depending on the request URL:
This is our IGTVGController
interface:
All we have to do now is create implementations of the IGTVGController
interface, retrieving data from the services and processing templates using the TemplateEngine
object.
In the end, it will look like this:
But first let's see how that template engine is initialized.
2.2 Creating and configuring the Template Engine
The process(…) method in our filter contained this sentence:
Which means that the GTVGApplication class is in charge of creating and configuring one of the most important objects in a Thymeleaf-enabled application: The TemplateEngine
instance.
Our org.thymeleaf.TemplateEngine
object is initialized like this:
Of course there are many ways of configuring a TemplateEngine
object, but for now these few lines of code will teach us enough about the steps needed.
The Template Resolver
Let's start with the Template Resolver:
Template Resolvers are objects that implement an interface from the Thymeleaf API called org.thymeleaf.templateresolver.ITemplateResolver
:
These objects are in charge of determining how our templates will be accessed, and in this GTVG application, the org.thymeleaf.templateresolver.ServletContextTemplateResolver
implementation that we are using specifies that we are going to retrieve our template files as resources from the Servlet Context: an application-wide javax.servlet.ServletContext
object that exists in every Java web application, and that resolves resources considering the web application root as the root for resource paths.
But that's not all we can say about the template resolver, because we can set some configuration parameters on it. First, the template mode, one of the standard ones:
XHTML is the default template mode for ServletContextTemplateResolver
, but it is good practice to establish it anyway so that our code documents clearly what is going on.
These prefix and suffix do exactly what it looks like: modify the template names that we will be passing to the engine for obtaining the real resource names to be used.
Using this configuration, the template name 'product/list' would correspond to:
Optionally, the amount of time that a parsed template living in cache will be considered valid can be configured at the Template Resolver by means of the cacheTTLMs property:
Of course, a template can be expelled from cache before that TTL is reached if the max cache size is reached and it is the oldest entry currently cached.
Cache behaviour and sizes can be defined by the user by implementing the ICacheManager
interface or simply modifying the StandardCacheManager
object set to manage caches by default.
We will learn more about template resolvers later. Now let's have a look at the creation of our Template Engine object.
The Template Engine
Template Engine objects are of class org.thymeleaf.TemplateEngine, and these are the lines that created our engine in the current example:
Rather simple, isn't it? All we need is to create an instance and set the Template Resolver to it.
A template resolver is the only required parameter a TemplateEngine
needs, although of course there are many others that will be covered later (message resolvers, cache sizes, etc). For now, this is all we need.
Our Template Engine is now ready and we can start creating our pages using Thymeleaf.
3.1 A multi-language welcome
Our first task will be to create a home page for our grocery site.
The first version we will write of this page will be extremely simple: just a title and a welcome message. This is our /WEB-INF/templates/home.html
file:
The first thing you will notice here is that this file is XHTML that can be correctly displayed by any browser, because it does not include any non-XHTML tags (and browsers ignore all attributes they don't understand, like th:text
). Also, browsers will display it in standards mode (not in quirks mode), because it has a well-formed DOCTYPE
declaration.
Next, this is also valid XHTML2, because we have specified a Thymeleaf DTD which defines attributes like th:text
so that your templates can be considered valid. And even more: once the template is processed (and all th:*
attributes are removed), Thymeleaf will automatically substitute that DTD declaration in the DOCTYPE
clause by a standard XHTML 1.0 Strict
one (we will leave this DTD translation features for a later chapter).
A thymeleaf namespace is also being declared for th:*
attributes:
Note that, if we hadn't cared about our template's validity or well-formedness at all, we could have simply specified a standard XHTML 1.0 Strict DOCTYPE
, along with no xmlns namespace declarations:
…and this would still be perfectly processable by Thymeleaf in the XHTML mode (although probably our IDE would make our life quite miserable showing warnings everywhere).
But enough about validation. Now for the really interesting part of the template: let's see what that th:text
attribute is about.
Using th:text and externalizing text
Externalizing text is extracting fragments of template code out of template files so that they can be kept in specific separate files (typically .properties
files) and that they can be easily substituted by equivalent texts written in other languages (a process called internationalization or simply i18n). Externalized fragments of text are usually called 'messages'.
Messages have always a key that identifies them, and Thymeleaf allows you to specify that a text should correspond to a specific message with the #{..}
syntax:
What we can see here are in fact two different features of the Thymeleaf Standard Dialect:
- The
th:text
attribute, which evaluates its value expression and sets the result of this evaluation as the body of the tag it is in, effectively substituting that 'Welcome to our grocery store!' text we see in the code. - The
#{home.welcome}
expression, specified in the Standard Expression Syntax, specifying that the text to be used by theth:text
attribute should be the message with thehome.welcome
key corresponding to whichever locale we are processing the template with.
Now, where is this externalized text?
The location of externalized text in Thymeleaf is fully configurable, and it will depend on the specific org.thymeleaf.messageresolver.IMessageResolver
implementation being used. Normally, an implementation based on .properties
files will be used, but we could create our own implementations if we wanted, for example, to obtain messages from a database.
However, we have not specified a message resolver to our Template Engine during initialization, and that means that our application is using the Standard Message Resolver, implemented by class org.thymeleaf.messageresolver.StandardMessageResolver
.
This standard message resolver expects to find messages for /WEB-INF/templates/home.html
in .properties files in the same folder and with the same name as the template, like:
/WEB-INF/templates/home_en.properties
for English texts./WEB-INF/templates/home_es.properties
for Spanish language texts./WEB-INF/templates/home_pt_BR.properties
for Portuguese (Brazil) language texts./WEB-INF/templates/home.properties
for default texts (if locale is not matched).
Let's have a look at our home_es.properties
file:
This is all we need for making Thymeleaf process our template. Let's create our Home controller then.
Contexts
In order to process our template, we will create a HomeController
class implementing the IGTVGController
interface we saw before:
The first thing we can see here is the creation of a context. A Thymeleaf context is an object implementing the org.thymeleaf.context.IContext
interface. Contexts should contain all the data required for an execution of the Template Engine in a variables map, and also reference the Locale that must be used for externalized messages.
There is a specialized extension of this interface, org.thymeleaf.context.IWebContext
:
The Thymeleaf core library offers an implementation of each of these interfaces:
org.thymeleaf.context.Context
implementsIContext
org.thymeleaf.context.WebContext
implementsIWebContext
And as you can see in the controller code, WebContext
is the one we will use. In fact we have to, because the use of a ServletContextTemplateResolver
requires that we use a context implementing IWebContext
.
Only two of those three constructor arguments are required, because the default locale for the system will be used if none is specified (although you should never let this happen in real applications).
From the interface definition we can tell that WebContext
will offer specialized methods for obtaining the request parameters and request, session and application attributes . But in fact WebContext
will do a little bit more than just that:
- Add all the request attributes to the context variables map.
- Add a context variable called
param
containing all the request parameters. - Add a context variable called
session
containing all the session attributes. - Add a context variable called
application
containing all the ServletContext attributes.
Just before execution, a special variable is set into all context objects (implementations of IContext
), including both Context
and WebContext
, called the execution info (execInfo
). This variable contains two pieces of data that can be used from within your templates:
- The template name (
${execInfo.templateName}
), the name specified for engine execution, and corresponding to the template being executed. - The current date and time (
${execInfo.now}
), aCalendar
object corresponding to the moment the template engine started its execution for this template.
Executing the template engine
With our context object ready, all we need is executing the template engine specifying the template name and the context, and passing on the response writer so that the response can be written to it:
Let's see the results of this using the Spanish locale:
3.2 More on texts and variables
Unescaped Text
The simplest version of our Home page seems to be ready now, but there is something we have not thought about… what if we had a message like this?
If we execute this template like before, we will obtain:
Which is not exactly what we expected, because our tag has been escaped and therefore it will be displayed at the browser.
This is the default behaviour of the th:text attribute. If we want Thymeleaf to respect our XHTML tags and not escape them, we will have to use a different attribute: th:utext
(for 'unescaped text'):
Using and displaying variables
Now let's add some more contents to our home page. For example, we could want to display the date below our welcome message, like this:
First of all, we will have to modify our controller so that we add that date as a context variable:
We have added a String
today variable to our context, and now we can display it in our template:
As you can see, we are still using the th:text
attribute for the job (and that's correct, because we want to substitute the tag's body), but the syntax is a little bit different this time and instead of a #{..}
expression value, we are using a ${..}
one. This is a variable expression value, and it contains an expression in a language called OGNL (Object-Graph Navigation Language) that will be executed on the context variables map.
The ${today}
expression simply means 'get the variable called today', but these expressions could be more complex (like ${user.name}
for 'get the variable called user, and call its getName()
method').
There are quite a lot of possibilities in attribute values: messages, variable expressions… and quite a lot more. Next chapter will show us what all these possibilities are.
We will make a small break in the development of our grocery virtual store to learn about one of the most important parts of the Thymeleaf Standard Dialect: the Thymeleaf Standard Expression syntax.
We have already seen two types of valid attribute values expressed in this syntax: message and variable expressions:
But there are more types of value we don't know yet, and more interesting detail to know about the ones we already know. First, let's see a quick summary of the Standard Expression features:
- Simple expressions:
- Variable Expressions:
${..}
- Selection Variable Expressions:
*{..}
- Message Expressions:
#{..}
- Link URL Expressions:
@{..}
- Variable Expressions:
- Literals
- Text literals:
'one text'
,'Another one!'
,… - Number literals:
0
,34
,3.0
,12.3
,… - Boolean literals:
true
,false
- Null literal:
null
- Literal tokens:
one
,sometext
,main
,…
- Text literals:
- Text operations:
- String concatenation:
+
- Literal substitutions:
|The name is ${name}|
- String concatenation:
- Arithmetic operations:
- Binary operators:
+
,-
,*
,/
,%
- Minus sign (unary operator):
-
- Binary operators:
- Boolean operations:
- Binary operators:
and
,or
- Boolean negation (unary operator):
!
,not
- Binary operators:
- Comparisons and equality:
- Comparators:
>
,<
,>=
,<=
(gt
,lt
,ge
,le
) - Equality operators: ,
!=
(eq
,ne
)
- Comparators:
- Conditional operators:
- If-then:
(if) ? (then)
- If-then-else:
(if) ? (then) : (else)
- Default:
(value) ?: (defaultvalue)
- If-then:
All these features can be combined and nested:
4.1 Messages
As we already know, #{..}
message expressions allow us to link this:
…to this:
But there's one aspect we still haven't thought of: what happens if the message text is not completely static? What if, for example, our application knew who is the user visiting the site at any moment and we wanted to greet him/her by name?
This means we would need to add a parameter to our message. Just like this:
Parameters are specified according to the java.text.MessageFormat
standard syntax, which means you could add format to numbers and dates as specified in the API docs for that class.
In order to specify a value for our parameter, and given an HTTP session attribute called user
, we would have:
If needed, several parameters could be specified, separated by commas. In fact, the message key itself could come from a variable:
4.2 Variables
We already mentioned that ${..}
expressions are in fact OGNL (Object-Graph Navigation Language) expressions executed on the map of variables contained in the context.
For detailed info about OGNL syntax and features, you should read the OGNL Language Guide at: http://commons.apache.org/ognl/
From OGNL's syntax, we know that this:
…is in fact equivalent to this:
But OGNL allows us to create quite more powerful expressions, and that's how this:
…does in fact obtain the user name by executing:
But getter method navigation is just one of OGNL's features. Let's see some more:
Expression Basic Objects
When evaluating OGNL expressions on the context variables, some objects are made available to expressions for higher flexibility. These objects will be referenced (per OGNL standard) starting with the #
symbol:
#ctx
: the context object.#vars:
the context variables.#locale
: the context locale.#httpServletRequest
: (only in Web Contexts) theHttpServletRequest
object.#httpSession
: (only in Web Contexts) theHttpSession
object.
So we can do this:
You can read the full reference of these objects in the Appendix A.
Expression Utility Objects
Besides these basic objects, Thymeleaf will offer us a set of utility objects that will help us perform common tasks in our expressions.
#dates
: utility methods forjava.util.Date
objects: formatting, component extraction, etc.#calendars
: analogous to#dates
, but forjava.util.Calendar
objects.#numbers
: utility methods for formatting numeric objects.#strings
: utility methods forString
objects: contains, startsWith, prepending/appending, etc.#objects
: utility methods for objects in general.#bools
: utility methods for boolean evaluation.#arrays
: utility methods for arrays.#lists
: utility methods for lists.#sets
: utility methods for sets.#maps
: utility methods for maps.#aggregates
: utility methods for creating aggregates on arrays or collections.#messages
: utility methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.#ids
: utility methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).
You can check what functions are offered by each of these utility objects in the Appendix B.
Reformatting dates in our home page
Now we know about these utility objects, we could use them to change the way in which we show the date in our home page. Instead of doing this in our HomeController
:
…we can do just this:
…and then perform date formatting in the view layer itself:
4.3 Expressions on selections (asterisk syntax)
Variable expressions not only can be written in ${..}
expressions, but also in *{..}
ones.
There is an important difference, though: the asterisk syntax evaluates expressions on selected objects rather than on the whole context variables map. This is: as long as there is no selected object, the dollar and the asterisk syntaxes do exactly the same.
And what is that object selection thing? A th:object
attribute. Let's use it in our user profile (userprofile.html
) page:
Which is exactly equivalent to:
Of course, dollar and asterisk syntax can be mixed:
When an object selection is in place, the selected object will be also available to dollar expressions as the #object
expression variable:
As said, if no object selection has been performed, dollar and asterisk syntaxes are exactly equivalent.
4.4 Link URLs
Because of their importance, URLs are first-class citizens in web application templates, and the Thymeleaf Standard Dialect has a special syntax for them, the @
syntax: @{..}
There are different types of URLs:
- Absolute URLs, like
http://www.thymeleaf.org
- Relative URLs, which can be:
- Page-relative, like
user/login.html
- Context-relative, like
/itemdetails?id=3
(context name in server will be added automatically) - Server-relative, like
~/billing/processInvoice
(allows calling URLs in another context (= application) in the same server. - Protocol-relative URLs, like
//code.jquery.com/jquery-2.0.3.min.js
- Page-relative, like
Thymeleaf can handle absolute URLs in any situation, but for relative ones it will require you to use a context object that implements the IWebContext
interface, which contains some info coming from the HTTP request and needed to create relative links.
Let's use this new syntax. Meet the th:href
attribute:
Some things to note here:
th:href
is an attribute modifier attribute: once processed, it will compute the link URL to be used and set the href attribute of thetag to this URL.
- We are allowed to use expressions for URL parameters (as you can see in
orderId=${o.id}
). The required URL-encoding operations will also be automatically performed. - If several parameters are needed, these will be separated by commas like
@{/order/process(execId=${execId},execType='FAST')}
- Variable templates are also allowed in URL paths, like
@{/order/{orderId}/details(orderId=${orderId})}
- Relative URLs starting with
/
(like/order/details
) will be automatically prefixed the application context name. - If cookies are not enabled or this is not yet known, a
';jsessionid=..'
suffix might be added to relative URLs so that session is preserved. This is called URL Rewriting, and Thymeleaf allows you to plug in your own rewriting filters by using theresponse.encodeURL(..)
mechanism from the Servlet API for every URL. - The
th:href
tag allowed us to (optionally) have a working statichref
attribute in our template, so that our template links remained navigable by a browser when opened directly for prototyping purposes.
A menu for our home page
Server root relative URLs
4.5 Literals
Numeric literals look exactly like what they are: numbers.
The boolean literals are true
and false
. For example:
The null
literal can be also used:
Numeric, boolean and null literals are in fact a particular case of literal tokens.
The nice part? Tokens don't need any quotes surrounding them. So we can do this:
4.6 Appending texts
4.7 Literal substitutions
These substitutions must be surrounded by vertical bars (|
), like:
Which is actually equivalent to:
Literal substitutions can be combined with other types of expressions:
4.8 Arithmetic operations
Some arithmetic operations are also available: +
, -
, *
, /
and %
.
Note that textual aliases exist for some of these operators: div
(/
), mod
(%
).
4.9 Comparators and Equality
4.10 Conditional expressions
Conditional expressions can also be nested using parentheses:
4.11 Default expressions (Elvis operator)
Let's see it in action in our user profile page:
As with conditional values, they can contain nested expressions between parentheses:
4.12 Preprocessing
…and a Messages_es.properties equivalent
:
Note that the preprocessing step for a French locale will be creating the following equivalent:
The preprocessing String __
can be escaped in attributes using __
.
5.1 Setting the value of any attribute
Given the required messages files, this will output:
5.2 Setting value to specific attributes
By now, you might be thinking that something like:
This looks much better!. Let's try and do the same to the action
attribute in the form
tag:
th:abbr | th:accept | th:accept-charset |
th:accesskey | th:action | th:align |
th:alt | th:archive | th:audio |
th:autocomplete | th:axis | th:background |
th:bgcolor | th:border | th:cellpadding |
th:cellspacing | th:challenge | th:charset |
th:cite | th:class | th:classid |
th:codebase | th:codetype | th:cols |
th:colspan | th:compact | th:content |
th:contenteditable | th:contextmenu | th:data |
th:datetime | th:dir | th:draggable |
th:dropzone | th:enctype | th:for |
th:form | th:formaction | th:formenctype |
th:formmethod | th:formtarget | th:frame |
th:frameborder | th:headers | th:height |
th:high | th:href | th:hreflang |
th:hspace | th:http-equiv | th:icon |
th:id | th:keytype | th:kind |
th:label | th:lang | th:list |
th:longdesc | th:low | th:manifest |
th:marginheight | th:marginwidth | th:max |
th:maxlength | th:media | th:method |
th:min | th:name | th:optimum |
th:pattern | th:placeholder | th:poster |
th:preload | th:radiogroup | th:rel |
th:rev | th:rows | th:rowspan |
th:rules | th:sandbox | th:scheme |
th:scope | th:scrolling | th:size |
th:sizes | th:span | th:spellcheck |
th:src | th:srclang | th:standby |
th:start | th:step | th:style |
th:summary | th:tabindex | th:target |
th:title | th:type | th:usemap |
th:value | th:valuetype | th:vspace |
th:width | th:wrap | th:xmlbase |
th:xmllang | th:xmlspace |
5.3 Setting more than one value at a time
![Switcher Switcher](https://cdn.contexts.co/static/website/media/feature--multiple-displays-support__2.jpg)
th:alt-title
will setalt
andtitle
.th:lang-xmllang
will setlang
andxml:lang
.
For our GTVG home page, this will allow us to substitute this:
…or this, which is equivalent:
…by this:
5.4 Appending and prepending
Working in an equivalent way to th:attr
, Thymeleaf offers the th:attrappend
and th:attrprepend
attributes, which append (suffix) or prepend (prefix) the result of their evaluation to the existing attribute values.
For example, you might want to store the name of a CSS class to be added (not set, just added) to one of your buttons in a context variable, because the specific CSS class to be used would depend on something that the user did before. Easy:
If you process this template with the cssStyle
variable set to 'warning'
, you will get:
There are also two specific appending attributes in the Standard Dialect: the th:classappend
and th:styleappend
attributes, which are used for adding a CSS class or a fragment of style to an element without overwriting the existing ones:
(Don't worry about that th:each
attribute. It is an iterating attribute and we will talk about it later.)
5.5 Fixed-value boolean attributes
Some XHTML/HTML5 attributes are special in that, either they are present in their elements with a specific and fixed value, or they are not present at all.
For example, checked
:
No other value than 'checked'
is allowed according to the XHTML standards for the checked
attribute (HTML5 rules are a little more relaxed on that). And the same happens with disabled
, multiple
, readonly
and selected
.
The Standard Dialect includes attributes that allow you to set these attributes by evaluating a condition, so that if evaluated to true, the attribute will be set to its fixed value, and if evaluated to false, the attribute will not be set:
The following fixed-value boolean attributes exist in the Standard Dialect:
th:async | th:autofocus | th:autoplay |
th:checked | th:controls | th:declare |
th:default | th:defer | th:disabled |
th:formnovalidate | th:hidden | th:ismap |
th:loop | th:multiple | th:novalidate |
th:nowrap | th:open | th:pubdate |
th:readonly | th:required | th:reversed |
th:scoped | th:seamless | th:selected |
5.6 Support for HTML5-friendly attribute and element names
It is also possible to use a completely different syntax to apply processors to your templates, more HTML5-friendly.
The data-{prefix}-{name}
syntax is the standard way to write custom attributes in HTML5, without requiring developers to use any namespaced names like th:*
. Thymeleaf makes this syntax automatically available to all your dialects (not only the Standard ones).
There is also a syntax to specify custom tags: {prefix}-{name}
, which follows the W3C Custom Elements specification (a part of the larger W3C Web Components spec). This can be used, for example, for the th:block
element (or also th-block
), which will be explained in a later section.
Important: this syntax is an addition to the namespaced th:*
one, it does not replace it. There is no intention at all to deprecate the namespaced syntax in the future.
So far we have created a home page, a user profile page and also a page for letting users subscribe to our newsletter… but what about our products? Shouldn't we build a product list to let visitors know what we sell? Well, obviously yes. And there we go now.
6.1 Iteration basics
For listing our products in our /WEB-INF/templates/product/list.html
page we will need a table. Each of our products will be displayed in a row (a element), and so for our template we will need to create a template row —one that will exemplify how we want each product to be displayed— and then instruct Thymeleaf to iterate it once for each product.
The Standard Dialect offers us an attribute for exactly that, th:each
.
Using th:each
For our product list page, we will need a controller that retrieves the list of products from the service layer and adds it to the template context:
And then we will use th:each
in our template to iterate the list of products:
That prod : ${prods}
attribute value you see above means 'for each element in the result of evaluating ${prods}
, repeat this fragment of template setting that element into a variable called prod'. Let's give a name each of the things we see:
- We will call
${prods}
the iterated expression or iterated variable. - We will call
prod
the iteration variable or simply iter variable.
Note that the prod
iter variable will only be available inside the element (including inner tags like
).
Iterable values
Not only java.util.List
objects can be used for iteration in Thymeleaf. In fact, there is a quite complete set of objects that are considered iterable by a th:each
attribute:
- Any object implementing
java.util.Iterable
- Any object implementing
java.util.Map
. When iterating maps, iter variables will be of classjava.util.Map.Entry
. - Any array
- Any other object will be treated as if it were a single-valued list containing the object itself.
6.2 Keeping iteration status
When using th:each,
Thymeleaf offers a mechanism useful for keeping track of the status of your iteration: the status variable.
Status variables are defined within a th:each
attribute and contain the following data:
- The current iteration index, starting with 0. This is the
index
property. - The current iteration index, starting with 1. This is the
count
property. - The total amount of elements in the iterated variable. This is the
size
property. - The iter variable for each iteration. This is the
current
property. - Whether the current iteration is even or odd. These are the
even/odd
boolean properties. - Whether the current iteration is the first one. This is the
first
boolean property. - Whether the current iteration is the last one. This is the
last
boolean property.
Let's see how we could use it within the previous example:
As you can see, the status variable (iterStat
in this example) is defined in the th:each
attribute by writing its name after the iter variable itself, separated by a comma. As happens to the iter variable, the status variable will only be available inside the fragment of code defined by the tag holding the th:each
attribute.
Let's have a look at the result of processing our template:
Note that our iteration status variable has worked perfectly, establishing the odd
CSS class only to odd rows (row counting starts with 0).
All those colspan and rowspan attributes in the tags, as well as the shape one in
are automatically added by Thymeleaf in accordance with the DTD for the selected XHTML 1.0 Strict standard, that establishes those values as default for those attributes (remember that our template didn't set a value for them). Don't worry about them at all, because they will not affect the display of your page. As an example, if we were using HTML5 (which has no DTD), those attributes would never be added.
7.1 Simple conditionals: 'if' and 'unless'
In order to do this, we would use the th:if
attribute:
Quite a lot of things to see here, so let's focus on the important line:
Perfect! That's exactly what we wanted.
- If value is not null:
- If value is a boolean and is
true
. - If value is a number and is non-zero
- If value is a character and is non-zero
- If value is a String and is not 'false', 'off' or 'no'
- If value is not a boolean, a number, a character or a String.
- If value is a boolean and is
- (If value is null, th:if will evaluate to false).
7.2 Switch statements
They work exactly as you would expect:
The default option is specified as th:case='*'
:
8.1 Including template fragments
Defining and referencing fragments
'templatename::domselector'
or the equivalenttemplatename::[domselector]
Includes the fragment resulting from executing the specified DOM Selector on the template namedtemplatename
.- Note that
domselector
can be a mere fragment name, so you could specify something as simple astemplatename::fragmentname
like in thefooter :: copy
above.
DOM Selector syntax is similar to XPath expressions and CSS selectors, see the Appendix C for more info on this syntax.
- Note that
'templatename'
Includes the complete template namedtemplatename
.::domselector'
or'this::domselector'
Includes a fragment from the same template.
Referencing fragments without th:fragment
Difference between th:include
and th:replace
…will result in:
The th:substituteby
attribute can also be used as an alias for th:replace
, but the latter is recommended. Note that th:substituteby
might be deprecated in future versions.
8.2 Parameterizable fragment signatures
In order to create a more function-like mechanism for the use of template fragments, fragments defined with th:fragment
can specify a set of parameters:
This requires the use of one of these two syntaxes to call the fragment from th:include
, th:replace
:
Note that order is not important in the last option:
###Fragment local variables without fragment signature
Even if fragments are defined without signature, like this:
We could use the second syntax specified above to call them (and only the second one):
This would be, in fact, equivalent to a combination of th:include
and th:with
:
Note that this specification of local variables for a fragment —no matter whether it has a signature or not— does not cause the context to emptied previously to its execution. Fragments will still be able to access every context variable being used at the calling template like they currently are.
###th:assert for in-template assertions
The th:assert
attribute can specify a comma-separated list of expressions which should be evaluated and produce true for every evaluation, raising an exception if not.
This comes in handy for validating parameters at a fragment signature:
8.3 Removing template fragments
Let's revisit the last version of our product list template:
This code is just fine as a template, but as a static page (when directly open by a browser without Thymeleaf processing it) it would not make a nice prototype.
Why? Because although perfectly displayable by browsers, that table only has a row, and this row has mock data. As a prototype, it simply wouldn't look realistic enough… we should have more than one product, we need more rows.
So let's add some:
Ok, now we have three, definitely better for a prototype. But… what will happen when we process it with Thymeleaf?:
The last two rows are mock rows! Well, of course they are: iteration was only applied to the first row, so there is no reason why Thymeleaf should have removed the other two.
We need a way to remove those two rows during template processing. Let's use the th:remove
attribute on the second and third tags:
Once processed, everything will look again as it should:
And what about that all
value in the attribute, what does it mean? Well, in fact th:remove
can behave in five different ways, depending on its value:
all
: Remove both the containing tag and all its children.body
: Do not remove the containing tag, but remove all its children.tag
: Remove the containing tag, but do not remove its children.all-but-first
: Remove all children of the containing tag except the first one.none
: Do nothing. This value is useful for dynamic evaluation.
What can that all-but-first
value be useful for? It will let us save some th:remove='all'
when prototyping:
The th:remove
attribute can take any Thymeleaf Standard Expression, as long as it returns one of the allowed String values (all
, tag
, body
, all-but-first
or none
).
This means removals could be conditional, like:
Also note that th:remove
considers null
a synonym to none
, so that the following works exactly as the example above:
In this case, if ${condition}
is false, null
will be returned, and thus no removal will be performed.
Thymeleaf calls local variables those variables that are defined for a specific fragment of a template, and are only available for evaluation inside that fragment.
An example we have already seen is the prod
iter variable in our product list page:
That prod
variable will be available only within the bonds of the tag. Specifically:
- It will be available for any other
th:*
attributes executing in that tag with less precedence thanth:each
(which means they will execute afterth:each
). - It will be available for any child element of the
tag, such as
elements.
Thymeleaf offers you a way to declare local variables without iteration. It is the th:with
attribute, and its syntax is like that of attribute value assignments:
When th:with
is processed, that firstPer
variable is created as a local variable and added to the variables map coming from the context, so that it is as available for evaluation as any other variables declared in the context from the beginning, but only within the bounds of the containing
You can define several variables at the same time using the usual multiple assignment syntax:
The th:with
attribute allows reusing variables defined in the same attribute:
Let's use this in our Grocery's home page! Remember the code we wrote for outputting a formatted date?
Well, what if we wanted that 'dd MMMM yyyy'
to actually depend on the locale? For example, we might want to add the following message to our home_en.properties
:
…and an equivalent one to our home_es.properties
:
Now, let's use th:with
to get the localized date format into a variable, and then use it in our th:text
expression:
That was clean and easy. In fact, given the fact that th:with
has a higher precedence
than th:text
, we could have solved this all in the span
tag:
You might be thinking: Precedence? We haven't talked about that yet! Well, don't worry because that is exactly what the next chapter is about.
What happens when you write more than one th:*
attribute in the same tag? For example:
Of course, we would expect that th:each
attribute to execute before the th:text
so that we get the results we want, but given the fact that the DOM (Document Object Model) standard does not give any kind of meaning to the order in which the attributes of a tag are written, a precedence mechanism has to be established in the attributes themselves in order to be sure that this will work as expected.
So, all Thymeleaf attributes define a numeric precedence, which establishes the order in which they are executed in the tag. This order is:
Order | Feature | Attributes |
---|---|---|
1 | Fragment inclusion | th:include th:replace |
2 | Fragment iteration | th:each |
3 | Conditional evaluation | th:if th:unless th:switch th:case |
4 | Local variable definition | th:object th:with |
5 | General attribute modification | th:attr th:attrprepend th:attrappend |
6 | Specific attribute modification | th:value th:href th:src .. |
7 | Text (tag body modification) | th:text th:utext |
8 | Fragment specification | th:fragment |
9 | Fragment removal | th:remove |
This precedence mechanism means that the above iteration fragment will give exactly the same results if the attribute position is inverted (although it would be slightly less readable):
12.1 Text inlining
Although the Standard Dialect allows us to do almost everything we might need by using tag attributes, there are situations in which we could prefer writing expressions directly into our HTML texts. For example, we could prefer writing this:
…instead of this:
Expressions between [[..]]
are considered expression inlining in Thymeleaf, and in them you can use any kind of expression that would also be valid in a th:text
attribute.
In order for inlining to work, we must activate it by using the th:inline
attribute, which has three possible values or modes (text
, javascript
and none
). Let's try text
:
The tag holding the th:inline
does not have to be the one containing the inlined expression/s, any parent tag would do:
So you might now be asking: Why aren't we doing this from the beginning? It's less code than all thoseth:text
attributes! Well, be careful there, because although you might find inlining quite interesting, you should always remember that inlined expressions will be displayed verbatim in your HTML files when you open them statically, so you probably won't be able to use them as prototypes anymore!
The difference between how a browser would statically display our fragment of code without using inlining…
…and using it…
…is quite clear.
12.2 Script inlining (JavaScript and Dart)
Thymeleaf offers a series of 'scripting' modes for its inlining capabilities, so that you can integrate your data inside scripts created in some script languages.
Current scripting modes are javascript
(th:inline='javascript'
) and dart
(th:inline='dart'
).
The first thing we can do with script inlining is writing the value of expressions into our scripts, like:
The /*[[..]]*/
syntax, instructs Thymeleaf to evaluate the contained expression. But there are more implications here:
- Being a javascript comment (
/*..*/
), our expression will be ignored when displaying the page statically in a browser. - The code after the inline expression (
'Sebastian'
) will be executed when displaying the page statically. - Thymeleaf will execute the expression and insert the result, but it will also remove all the code in the line after the inline expression itself (the part that is executed when displayed statically).
So, the result of executing this will be:
You can also do it without comments with the same effects, but that will make your script to fail when loaded statically:
Note that this evaluation is intelligent and not limited to Strings. Thymeleaf will correctly write in Javascript/Dart syntax the following kinds of objects:
- Strings
- Numbers
- Booleans
- Arrays
- Collections
- Maps
- Beans (objects with getter and setter methods)
For example, if we had the following code:
That ${session.user}
expression will evaluate to a User
object, and Thymeleaf will correctly convert it to Javascript syntax:
Adding code
An additional feature when using javascript inlining is the ability to include code between a special comment syntax /*[+..+]*/
so that Thymeleaf will automatically uncomment that code when processing the template:
Will be executed as:
You can include expressions inside these comments, and they will be evaluated:
Removing code
It is also possible to make Thymeleaf remove code between special /*[- */
and /* -]*/
comments, like this:
13.1 Validating templates
As mentioned before, Thymeleaf offers us out-of-the-box two standard template modes that validate our templates before processing them: VALIDXML
and VALIDXHTML.
These modes require our templates to be not only well-formed XML (which they should always be), but in fact valid according to the specified DTD
.
The problem is that if we use the VALIDXHTML
mode with templates including a DOCTYPE
clause such as this:
…we are going to obtain validation errors because the th:*
tags do not exist according to that DTD.
That's perfectly normal, as the W3C obviously has no reason to include Thymeleaf's features in their standards but, how do we solve it? By changing the DTD.
Thymeleaf includes a set of DTD
files that mirror the original ones from the XHTML standards, but adding all the available th:*
attributes from the Standard Dialect. That's why we have been using this in our templates:
That SYSTEM
identifier instructs the Thymeleaf parser to resolve the special Thymeleaf-enabled XHTML 1.0 Strict DTD
file and use it for validating our template. And don't worry about that http
thing, because that is only an identifier, and the DTD
file will be locally read from Thymeleaf's jar files.
Note that because this DOCTYPE declaration is a perfectly valid one, if we open a browser to statically display our template as a prototype it will be rendered in Standards Mode.
Here you have the complete set of Thymeleaf-enabled DTD
declarations for all the supported flavours of XHTML:
Also note that, in order for your IDE to be happy, and even if you are not working in a validating mode, you will need to declare the th
namespace in your html
tag:
13.2 Doctype translation
It is fine for our templates to have a DOCTYPE
like:
But it would not be fine for our web applications to send XHTML documents with this DOCTYPE
to client browsers, because:
- They are not
PUBLIC
(they areSYSTEM DOCTYPE
s), and therefore our web would not be validatable with the W3C Validators. - They are not needed, because once processed, all
th:*
tags will have dissapeared.
That's why Thymeleaf includes a mechanism for DOCTYPE translation, which will automatically translate your thymeleaf-specific XHTML DOCTYPE
s into standard DOCTYPE
s.
For example, if your template is XHTML 1.0 Strict and looks like this:
After making Thymeleaf process the template, your resulting XHTML will look like this:
You don't have to do anything for these transformations to take place: Thymeleaf will take care of them automatically.
Now we know a lot about using Thymeleaf, we can add some new pages to our website for order management.
Note that we will focus on XHTML code, but you can have a look at the bundled source code if you want to see the corresponding controllers.
14.1 Order List
Let's start by creating an order list page, /WEB-INF/templates/order/list.html
:
Contexts 2 4 – Fast Window Switcher System
There's nothing here that should surprise us, except for this little bit of OGNL magic:
What that does is, for each order line (OrderLine
object) in the order, multiply its purchasePrice
and amount
properties (by calling the corresponding getPurchasePrice()
and getAmount()
methods) and return the result into a list of numbers, later aggregated by the #aggregates.sum(..)
function in order to obtain the order total price.
You've got to love the power of OGNL. Audfree tidal music converter 1 4 0 download free.
14.2 Order Details
Now for the order details page, in which we will make a heavy use of asterisk syntax:
Not much really new here, except for this nested object selection:
…which makes that *{name}
in fact equivalent to:
15.1 Template Resolvers
For our Good Thymes Virtual Grocery, we chose an ITemplateResolver
implementation called ServletContextTemplateResolver
that allowed us to obtain templates as resources from the Servlet Context.
Besides giving you the ability to create your own template resolver by implementing ITemplateResolver,
Thymeleaf includes three other implementations out of the box:
org.thymeleaf.templateresolver.ClassLoaderTemplateResolver
, which resolves templates as classloader resources, like:org.thymeleaf.templateresolver.FileTemplateResolver
, which resolves templates as files from the file system, like:org.thymeleaf.templateresolver.UrlTemplateResolver
, which resolves templates as URLs (even non-local ones), like:
All of the pre-bundled implementations of ITemplateResolver
allow the same set of configuration parameters, which include:
Prefix and suffix (as already seen):
Template aliases that allow the use of template names that do not directly correspond to file names. If both suffix/prefix and alias exist, alias will be applied before prefix/suffix:
Encoding to be applied when reading templates:
Default template mode, and patterns for defining other modes for specific templates:
Default mode for template cache, and patterns for defining whether specific templates are cacheable or not:
TTL in milliseconds for parsed template cache entries originated in this template resolver. If not set, the only way to remove an entry from the cache will be LRU (cache max size exceeded and the entry is the oldest).
Also, a Template Engine can be specified several template resolvers, in which case an order can be established between them for template resolution so that, if the first one is not able to resolve the template, the second one is asked, and so on:
When several template resolvers are applied, it is recommended to specify patterns for each template resolver so that Thymeleaf can quickly discard those template resolvers that are not meant to resolve the template, enhancing performance. Doing this is not a requirement, but an optimization:
15.2 Message Resolvers
We did not explicitly specify a Message Resolver implementation for our Grocery application, and as it was explained before, this meant that the implementation being used was an org.thymeleaf.messageresolver.StandardMessageResolver
object.
This StandardMessageResolver,
which looks for messages files with the same name as the template in the way already explained, is in fact the only message resolver implementation offered by Thymeleaf core out of the box, although of course you can create your own by just implementing the org.thymeleaf.messageresolver.IMessageResolver
interface.
The Thymeleaf + Spring integration packages offer an IMessageResolver
implementation which uses the standard Spring way of retrieving externalized messages, by using MessageSource
objects.
What if you wanted to add a message resolver (or more) to the Template Engine? Easy:
And why would you want to have more than one message resolver? for the same reason as template resolvers: message resolvers are ordered and if the first one cannot resolve a specific message, the second one will be asked, then the third, etc.
15.3 Logging
Thymeleaf pays quite a lot of attention to logging, and always tries to offer the maximum amount of useful information through its logging interface.
The logging library used is slf4j,
which in fact acts as a bridge to whichever logging implementation you might want to use in your application (for example, log4j
).
Thymeleaf classes will log TRACE
, DEBUG
and INFO
-level information, depending on the level of detail you desire, and besides general logging it will use three special loggers associated with the TemplateEngine class which you can configure separately for different purposes:
org.thymeleaf.TemplateEngine.CONFIG
will output detailed configuration of the library during initialization.org.thymeleaf.TemplateEngine.TIMER
will output information about the amount of time taken to process each template (useful for benchmarking!)org.thymeleaf.TemplateEngine.cache
is the prefix for a set of loggers that output specific information about the caches. Although the names of the cache loggers are configurable by the user and thus could change, by default they are:org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE
org.thymeleaf.TemplateEngine.cache.FRAGMENT_CACHE
org.thymeleaf.TemplateEngine.cache.MESSAGE_CACHE
org.thymeleaf.TemplateEngine.cache.EXPRESSION_CACHE
An example configuration for Thymeleaf's logging infrastructure, using log4j
, could be:
Thymeleaf works thanks to a DOM processing engine and a series of processors —one for each type of node that needs to apply logic— that modify the document's DOM tree in order to create the results you expect by combining this tree with your data.
It also includes —by default— a cache that stores parsed templates, this is, the DOM trees resulting from reading and parsing template files before processing them. This is especially useful when working in a web application, and builds on the following concepts:
- Input/Output is almost always the slowest part of any application. In-memory process is extremely quick compared to it.
- Cloning an existing in-memory DOM-tree is always much quicker than reading a template file, parsing it and creating a new DOM object tree for it.
- Web applications usually only have a few dozen templates.
- Template files are small-to-medium size, and they are not modified while the application is running.
This all leads to the idea that caching the most used templates in a web application is feasible without wasting big amounts of memory, and also that it will save a lot of time that would be spent on input/output operations on a small set of files that, in fact, never change.
And how can we take control of this cache? First, we've learned before that we can enable or disable it at the Template Resolver, even acting only on specific templates:
Also, we could modify its configuration by establishing our own Cache Manager object, which could be an instance of the default StandardCacheManager
implementation:
Refer to the javadoc API of org.thymeleaf.cache.StandardCacheManager
for more info on configuring the caches.
Entries can be manually removed from the template cache:
Some objects and variable maps are always available to be invoked at variable expressions (executed by OGNL or SpringEL). Let's see them:
Base objects
- #ctx : the context object. It will be an implementation of
org.thymeleaf.context.IContext
,org.thymeleaf.context.IWebContext
depending on our environment (standalone or web). If we are using the Spring integration module, it will be an instance oforg.thymeleaf.spring[3|4].context.SpringWebContext
.
- #locale : direct access to the
java.util.Locale
associated with current request.
#vars : an instance of
org.thymeleaf.context.VariablesMap
with all the variables in the Context (usually the variables contained in#ctx.variables
plus local ones).Unqualified expressions are evaluated against this object. In fact,
${something}
is completely equivalent to (but more beautiful than)${#vars.something}
.#root
is a synomyn for the same object.
Web context namespaces for request/session attributes, etc.
When using Thymeleaf in a web environment, we can use a series of shortcuts for accessing request parameters, session attributes and application attributes:
Note these are not context objects, but maps added to the context as variables, so we access them without #
. In some way, therefore, they act as namespaces.
- param : for retrieving request parameters.
${param.foo}
is aString[]
with the values of thefoo
request parameter, so${param.foo[0]}
will normally be used for getting the first value.
- session : for retrieving session attributes.
- application : for retrieving application/servlet context attributes.
Note there is no need to specify a namespace for accessing request attributes (as opposed to request parameters) because all request attributes are automatically added to the context as variables in the context root:
Web context objects
Inside a web environment there is also direct access to the following objects (note these are objects, not maps/namespaces):
- #httpServletRequest : direct access to the
javax.servlet.http.HttpServletRequest
object associated with the current request.
- #httpSession : direct access to the
javax.servlet.http.HttpSession
object associated with the current request.
Spring context objects
If you are using Thymeleaf from Spring, you can also access these objects:
- #themes : provides the same features as the Spring
spring:theme
JSP tag.
Spring beans
Thymeleaf also allows accessing beans registered at your Spring Application Context in the standard way defined by Spring EL, which is using the syntax @beanName
, for example:
Dates
- #dates : utility methods for
java.util.Date
objects:
Calendars
- #calendars : analogous to
#dates
, but forjava.util.Calendar
objects:
Numbers
- #numbers : utility methods for number objects:
Strings
- #strings : utility methods for
String
objects:
Objects
- #objects : utility methods for objects in general
Contexts 2 4 – Fast Window Switcher Kit
Booleans
- #bools : utility methods for boolean evaluation
Arrays
- #arrays : utility methods for arrays
Lists
- #lists : utility methods for lists
Sets
- #sets : utility methods for sets
Maps
- #maps : utility methods for maps
Aggregates
- #aggregates : utility methods for creating aggregates on arrays or collections
Messages
- #messages : utility methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using
#{..}
syntax.
IDs
- #ids : utility methods for dealing with
id
attributes that might be repeated (for example, as a result of an iteration).
DOM Selectors borrow syntax features from XPATH, CSS and jQuery, in order to provide a powerful and easy to use way to specify template fragments.
For example, the following selector will select every
content
, in every position inside the markup:The basic syntax inspired from XPath includes:
/x
means direct children of the current node with name x.//x
means children of the current node with name x, at any depth.x[@z='v']
means elements with name x and an attribute called z with value 'v'.x[@z1='v1' and @z2='v2']
means elements with name x and attributes z1 and z2 with values 'v1' and 'v2', respectively.x[i]
means element with name x positioned in number i among its siblings.x[@z='v'][i]
means elements with name x, attribute z with value 'v' and positioned in number i among its siblings that also match this condition.
But more concise syntax can also be used:
x
is exactly equivalent to//x
(search an element with name or referencex
at any depth level).Selectors are also allowed without element name/reference, as long as they include a specification of arguments. So
[@class='oneclass']
is a valid selector that looks for any elements (tags) with a class attribute with value 'oneclass'.
Advanced attribute selection features:
Besides
=
(equal), other comparison operators are also valid:!=
(not equal),^=
(starts with) and$=
(ends with). For example:x[@class^='section']
means elements with namex
and a value for attributeclass
that starts withsection
.Attributes can be specified both starting with
@
(XPath-style) and without (jQuery-style). Sox[z='v']
is equivalent tox[@z='v']
.Multiple-attribute modifiers can be joined both with
and
(XPath-style) and also by chaining multiple modifiers (jQuery-style). Sox[@z1='v1' and @z2='v2']
is actually equivalent tox[@z1='v1'][@z2='v2']
(and also tox[z1='v1'][z2='v2']
).
Direct jQuery-like selectors:
x.oneclass
is equivalent tox[class='oneclass']
..oneclass
is equivalent to[class='oneclass']
.x#oneid
is equivalent tox[id='oneid']
.#oneid
is equivalent to[id='oneid']
.x%oneref
means nodes -not just elements- with name x that match reference oneref according to a specifiedDOMSelector.INodeReferenceChecker
implementation.%oneref
means nodes -not just elements- with any name that match reference oneref according to a specifiedDOMSelector.INodeReferenceChecker
implementation. Note this is actually equivalent to simplyoneref
because references can be used instead of element names.Direct selectors and attribute selectors can be mixed:
a.external[@href^='https']
.
Contexts 2 4 – Fast Window Switcher Switch
The above DOM Selector expression:
could be written as:
###Multivalued class matching
DOM Selectors understand the class attribute to be multivalued, and therefore allow the application of selectors on this attribute even if the element has several class values.
For example, div[class='two']
will match
###Optional brackets
The syntax of the fragment inclusion attributes converts every fragment selection into a DOM selection, so brackets [..]
are not needed (though allowed).
So the following, with no brackets, is equivalent to the bracketed selector seen above:
Windows Switcher App
So, summarizing, this:
Contexts 2 4 – Fast Window Switcher Wiring
Will look for a th:fragment='myfrag'
fragment signature. But would also look for tags with name myfrag
if they existed (which they don't, in HTML). Note the difference with:
which will actually look for any elements with class='myfrag'
, without caring about th:fragment
signatures.
Given the fact that XHTML5 is just XML-formed HTML5 served with the application/xhtml+xml content type, we could also say that Thymeleaf supports XHTML5.↩
Note that, although this template is valid XHTML, we earlier selected template mode 'XHTML' and not 'VALIDXHTML'. For now, it will be OK for us to just have validation turned off – but at the same time we don't want our IDE to complain too much.↩