SlideShare una empresa de Scribd logo
1 de 45
Descargar para leer sin conexión
A modern Java templating language
Thymeleaf
Andre Azzolini!
Broadleaf Commerce!
FW JUG - July 2nd, 2014
Introduction
Who am I?!
‣ Graduated from The University of Texas at Austin (Computer Science)!
‣ Consultant at Credera!
‣ Senior Engineer at Broadleaf Commerce!
!
What is Broadleaf Commerce?!
‣ Open source Java enterprise eCommerce framework!
‣ Focus on extensibility and scalability!
‣ Based on Spring, Hibernate, Thymeleaf
‣ Why use Thymeleaf?!
‣ Thymeleaf Basics!
‣ Intermediate Thymeleaf!
‣ Existing Ecosystem!
‣ Broadleaf Use Cases
Agenda
‣ Not compiled —> short feedback loop!
‣ Natural templating —> closer to designers’ HTML!
‣ Modular architecture —> hooks for customization
Why use Thymeleaf?
Thymeleaf Basics
Installation in a Spring application
<bean id="templateResolver"
class="org.thymeleaf...ServletContextTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML5" />
</bean>
<bean id="templateEngine"
class="org.thymeleaf.spring3.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
</bean>
!
<bean class="org.thymeleaf.spring3.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
<property name="order" value="1" />
<property name="viewNames" value="*.html" />
</bean>
Outputting Values
———— HomeController.java ————
!
@RequestMapping("/")
public String viewHomepage(Model model) {
model.addAttribute("header", "My Header Message");
return "index";
}
!
———— /WEB-INF/templates/index.html ————
!
<span th:text="${header}">A Default Header</span>
!
———— Browser rendering ————
!
<span>A Default Header</span>
!
———— Thymeleaf rendering ————
!
<span>My Header Message</span>
Outputting Values (i18n)
———— /WEB-INF/templates/index.html ————
!
<span th:text="#{homepage.header}">A Default Header</span>
!
———— messages_en.properties ————
!
homepage.header=My Header Message
!
———— messages_es.properties ————
!
homepage.header=Mi Mensaje de Cabecera
!
———— Thymeleaf rendering (en) ————
!
<span>My Header Message</span>
!
———— Thymeleaf rendering (es) ————
!
<span>Mi Mensaje de Cabecera</span>
Outputting Values (i18n cont.)
———— HomeController.java ————
!
@RequestMapping("/")
public String viewHomepage(Model model) {
model.addAttribute("header", "homepage.header");
return "index";
}
!
———— /WEB-INF/templates/index.html ————
!
<span th:text="#{${header}}" />
Outputting Raw Values
———— HomeController.java ————
!
@RequestMapping("/")
public String viewHomepage(Model model) {
model.addAttribute("header", "<b>My Bolded Header</b>");
return "index";
}
!
———— Rendered with th:text ————
!
<span>&lt;b&gt;My Bolded Header&lt;/b&gt;</span>
!
———— Rendered with th:utext ————
!
<span><b>My Bolded Header</b></span>
Outputting Values Inline
———— index.html ————
!
<span th:inline="text">
[[${address.user?.firstName}]] [[${address.user?.lastName}]]<br />
[[${address.line1}]] <br />
[[${address.line2}]] <br />
[[${address.city}]], [[${address.state}]] [[${address.zipcode}]]
</span>
Scoping available variables
———— index.html ————
!
<span th:inline="text" th:object="${address}">
[[*{user?.firstName}]] [[${user?.lastName}]]<br />
[[*{line1}]] <br />
[[*{line2}]] <br />
[[*{city}]], [[*{state}]] [[*{zipcode}]]
</span>
Attribute Manipulation
———— index.html ————
!
<span th:class="${isEven ? 'even' : 'odd'}"
th:classappend="${isError ? 'error'}"
th:attr="data-row-id=${row.id}"
th:text="${row.description}" />
———— Rendered index.html ————
!
<span class="odd error" data-row-id="5">Row 5 Description</span>
Conditionals
———— index.html ————
!
<span th:if="${user.loggedIn}" th:text="${'Hi ' + user.name}" />
<span th:unless="${user.loggedIn}">Welcome guest</span>
!
———— messages_en.properties ————
!
header.greeting.user=Hi {0}
header.greeting.anonymous=Welcome guest
!
———— index.html (i18n) ————
!
<span th:if="…" th:text="#{header.greeting.user(${user.name})}" />
<span th:unless="…" th:text="#{header.greeting.anonymous}" />
Loops and Links
———— HomeController.java ————
!
@RequestMapping("/products")
public String viewProductListing(Model model) {
List<Product> products = productService.findAll();
model.addAttribute("products", products);
return "productListing";
}
!
———— productListing.html ————
!
<ul>
<li th:each="product : ${products}" th:object="${product}">
<img th:src="@{*{mainImage}}" /> <br />
<a th:href="@{*{link}}" th:text="*{name}" />
</li>
</ul>
Includes
———— productListing.html ————
!
<ul>
<li th:each="product : ${products}"
th:object="${product}"
th:include="components/productBlock" />
</ul>
!
———— components/productBlock.html ————

!
<img th:src="@{*{mainImage}}" /> <br />
<a th:href="@{*{link}}" th:text="*{name}" />
Variable Expressions
———— HomeController.java ————
!
@RequestMapping("/products")
public String viewProductListing(Model model) {
List<Product> products = productService.findAll();
model.addAttribute("products", products);
return "productListing";
}
!
———— productListing.html ————
!
<span th:text="${'Products Found: ' + #lists.size(products)}" />
‣ format(date, 'dd/MMM/yyyy HH:mm')!
‣ day(date), month(date), dayOfWeek(date), etc!
‣ create(year, month, day)!
‣ createNow(), createToday()
#dates Variable Expression
‣ formatInteger(num, 3, 'POINT')!
‣ sequence(from, to)!
‣ sequence(from, to, step)
#numbers Variable Expression
‣ isEmpty!
‣ contains!
‣ indexOf, substring, replace!
‣ prepend, append!
‣ toLowerCase, toUpperCase, capitalize, capitalizeWords!
‣ escapeXml, unescapeJava
#strings Variable Expression
‣ isEmpty, size!
‣ contains, containsAll!
‣ sort
#arrays, #lists, #sets, #maps Variable Expressions
#aggregates Variable Expression
———— Order.java ————
!
public class Order {
protected List<OrderLine> lines;
}
!
class OrderLine {
protected BigDecimal price;
protected int qty;
}
!
———— order.html ————
!
<span th:text="${#aggregates.sum(order.lines.{price * qty})}" />
Intermediate Thymeleaf
Custom Variable Expressions
———— SystemPropertyVariableExpression.java ————
!
@Resource protected SystemPropertyService service;
!
public String getName() {
return "sp";
}
!
public boolean getAsBoolean(String prop) {
return service.resolveAsBoolean(prop);
}
!
———— index.html ————
!
<span th:if="${#sp.getAsBoolean('pagination.enabled')}"
th:include="components/paginator" />
Spring Bean Direct Access
———— MyService.java ————
!
@Service
public class MyService {
!
public boolean isPalindrome(String str) {
return str.equals(StringUtils.reverse(str));
}
!
}
!
———— index.html ————
!
<span th:if="${@myService.isPalindrome('tacocat')}" />
Custom Processors
———— PriceProcessor.java ————
!
public class PriceProcessor extends
AbstractTextChildModifierAttrProcessor {
public PriceTextDisplayProcessor() {
super("price");
}
!
protected String getText(Arguments arguments, Element element,
String attributeName) {
Expression expression = ...
Object result = expression.execute(...);
if (result instanceof Money) {
return ((Money) result).getFormattedValue();
} else {
return result.toString();
}
}
}
Custom Processors (cont.)
———— MyCustomDialect.java ————
public class MyCustomDialect extends AbstractDialect {
private Set<IProcessor> processors = new HashSet<IProcessor>();
@Override public String getPrefix() {
return "mcd";
}
}
!
———— applicationContext.xml ————
!
<bean id="myCustomDialect" class="com.myco.MyCustomDialect">
<property name="processors">
<set>
<bean id="priceProcessor" class="com.myco.PriceProcessor">
</set>
</property>
</bean>
Custom Processors (cont.)
———— applicationContext.xml (cont.) ————
!
<bean id="templateEngine" class="...">
<property name="additionalDialects">
<set>
<bean class="com.myco.MyCustomDialect"/>
</set>
</property>
</bean>
!
———— components/productBlock.html ————

!
<img th:src="@{*{mainImage}}" /> <br />
<a th:href="@{*{link}}" th:text="*{name}" />
<span mcd:price="*{price}" />
Custom Processors (cont.)
public class FormProcessor extends AbstractElementProcessor {
!
protected ProcessorResult processElement(Arguments a, Element el) {
String method = el.getAttributeValue("method");
if (!"GET".equals(method)) {
String csrfToken = protectionService.getCsrfToken();
Element csrfNode = new Element("input");
csrfNode.setAttribute("type", "hidden");
csrfNode.setAttribute("name", "csrf-token");
csrfNode.setAttribute("value", csrfToken);
el.addChild(csrfNode);
}
!
Element newForm = el.cloneElementNode(...);
el.getParent().insertAfter(el, newForm);
el.getParent().removeChild(el);
return ProcessorResult.OK;
}
}
Custom Processors (cont.)
———— login.html ————
!
<mcd:form>
<input type="text" name="username" />
<input type="password" name="pass" />
</mcd:form>
!
———— Rendered login page ————
!
<form>
<input type="text" name="username" />
<input type="password" name="pass" />
<input type="hidden" name="csrf-token" value="L9ThxnotKPzthJ" />
</form>
Spring Form Binding
———— UserRegistrationForm.java ————
!
public class UserRegistrationForm {
!
protected String username;
protected String password;
protected String confirmPassword;
protected String email;
!
... getters / setters ...
!
}
Spring Form Binding (cont.)
———— UserRegistrationController.java ————
!
public class UserRegistrationController {
!
@RequsetMapping("/register", method = RequestMethod.GET)
public String showRegisterForm(Model model,
@ModelAttribute UserRegistrationForm registerForm) {
return "components/userRegistrationForm";
}
!
@RequsetMapping("/register", method = RequestMethod.POST)
public String showRegisterForm(Model model,
@ModelAttribute UserRegistrationForm registerForm) {
// register the user
return "redirect:/";
}
!
}
Spring Form Binding (cont.)
———— components/userRegistrationForm.html ————
!
<form th:action="@{/register}" th:object="${registerForm}"
method="POST">
<input type="text" th:field="*{username}" />
<input type="password" th:field="*{password}" />
<input type="password" th:field="*{confirmPassword}" />
<input type="text" th:field="*{email}" />
</form>
Existing Ecosystem
‣ Code completion for out of box processors!
‣ Content assist inside expressions!
‣ Ability to provide completion for custom processors
Eclipse IDE Plugin
‣ Use Thymeleaf templates in Tiles definitions!
‣ Mix JSP and Thymeleaf templates!
‣ Optional Spring MVC 3 and Spring Web Flow 2.3
integrations
Thymeleaf + Apache Tiles 2
‣ Lightweight dialect approach instead of Tiles!
‣ Uses decorators and fragments within the templates, so
there is no need for a Tiles definition file!
‣ Created by a core Thymeleaf contributor!
‣ I think it's more intuitive than the Tiles plugin
Layout Dialect (unofficial)
‣ sec:authorize processor (accepts normal Spring Security
expressions like hasRole('ROLE_ADMIN'))!
‣ Grab the current authentication object in expressions:
${#authentication.name}!
Spring Security 3
‣ Allows you to specify a cache attribute on a DOM
element!
‣ Caches the resulting HTML with the provided name!
‣ Doesn't require Thymeleaf to process the DOM for that
node when rendering
Cache Dialect (unofficial)
<ul cache:name="productsList" cache:ttl="60">
<li th:each="product : ${products}"
th:include="components/productBlock" />
</ul>
‣ JavaScript library for natural templating!
‣ Can process th:include without executing in an
application!
‣ Provides features for evaluating conditionals while
statically prototyping
Thymol (unofficial)
Broadleaf Use Cases
‣ Serve individual files in development, but a bundle in
production!
‣ Handle expiration of bundles!
‣ Dynamically modify contents of certain resources
JS / CSS Bundling
<blc:bundle name="admin.js"
files="BLC.js,
BLC-system-property.js,
blc-dates.js" />
‣ Intelligent auto-generation of cache keys!
‣ Provide ability to alter a portion of the cached,
generated HTML!
‣ Invalidate cache elements when things change in the
admin
Advanced Caching Strategy
‣ Allow users to modify templates through the admin
tool!
‣ Serve templates directly from the database!
‣ No need for compiling the templates like we would
have to with JSP
Database Template Resolution
Thanks!

Más contenido relacionado

La actualidad más candente

Hibernate Presentation
Hibernate  PresentationHibernate  Presentation
Hibernate Presentationguest11106b
 
Introduction to RxJS
Introduction to RxJSIntroduction to RxJS
Introduction to RxJSBrainhub
 
Introduction to Javascript
Introduction to JavascriptIntroduction to Javascript
Introduction to JavascriptAmit Tyagi
 
ES6 presentation
ES6 presentationES6 presentation
ES6 presentationritika1
 
Angular 2.0 forms
Angular 2.0 formsAngular 2.0 forms
Angular 2.0 formsEyal Vardi
 
Introduction to JSX
Introduction to JSXIntroduction to JSX
Introduction to JSXMicah Wood
 
Nodejs functions & modules
Nodejs functions & modulesNodejs functions & modules
Nodejs functions & modulesmonikadeshmane
 
Intro to Asynchronous Javascript
Intro to Asynchronous JavascriptIntro to Asynchronous Javascript
Intro to Asynchronous JavascriptGarrett Welson
 
Angular - Chapter 4 - Data and Event Handling
 Angular - Chapter 4 - Data and Event Handling Angular - Chapter 4 - Data and Event Handling
Angular - Chapter 4 - Data and Event HandlingWebStackAcademy
 
Grails object relational mapping: GORM
Grails object relational mapping: GORMGrails object relational mapping: GORM
Grails object relational mapping: GORMSaurabh Dixit
 
Asynchronous JavaScript Programming with Callbacks & Promises
Asynchronous JavaScript Programming with Callbacks & PromisesAsynchronous JavaScript Programming with Callbacks & Promises
Asynchronous JavaScript Programming with Callbacks & PromisesHùng Nguyễn Huy
 

La actualidad más candente (20)

Angular Observables & RxJS Introduction
Angular Observables & RxJS IntroductionAngular Observables & RxJS Introduction
Angular Observables & RxJS Introduction
 
Hibernate Presentation
Hibernate  PresentationHibernate  Presentation
Hibernate Presentation
 
Introduction to RxJS
Introduction to RxJSIntroduction to RxJS
Introduction to RxJS
 
Introduction to Javascript
Introduction to JavascriptIntroduction to Javascript
Introduction to Javascript
 
ES6 presentation
ES6 presentationES6 presentation
ES6 presentation
 
Angular 2.0 forms
Angular 2.0 formsAngular 2.0 forms
Angular 2.0 forms
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
Introduction to JSX
Introduction to JSXIntroduction to JSX
Introduction to JSX
 
Nodejs functions & modules
Nodejs functions & modulesNodejs functions & modules
Nodejs functions & modules
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
Core java
Core javaCore java
Core java
 
Intro to Asynchronous Javascript
Intro to Asynchronous JavascriptIntro to Asynchronous Javascript
Intro to Asynchronous Javascript
 
Angular 2 observables
Angular 2 observablesAngular 2 observables
Angular 2 observables
 
Angular Data Binding
Angular Data BindingAngular Data Binding
Angular Data Binding
 
Angular - Chapter 4 - Data and Event Handling
 Angular - Chapter 4 - Data and Event Handling Angular - Chapter 4 - Data and Event Handling
Angular - Chapter 4 - Data and Event Handling
 
Grails object relational mapping: GORM
Grails object relational mapping: GORMGrails object relational mapping: GORM
Grails object relational mapping: GORM
 
Spring & hibernate
Spring & hibernateSpring & hibernate
Spring & hibernate
 
Java 8 Lambda and Streams
Java 8 Lambda and StreamsJava 8 Lambda and Streams
Java 8 Lambda and Streams
 
Asynchronous JavaScript Programming with Callbacks & Promises
Asynchronous JavaScript Programming with Callbacks & PromisesAsynchronous JavaScript Programming with Callbacks & Promises
Asynchronous JavaScript Programming with Callbacks & Promises
 

Destacado

Introducing thymeleaf
Introducing thymeleafIntroducing thymeleaf
Introducing thymeleafeiryu
 
Thymeleaf Introduction
Thymeleaf IntroductionThymeleaf Introduction
Thymeleaf IntroductionAnthony Chen
 
Spring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
Spring I/O 2012: Natural Templating in Spring MVC with ThymeleafSpring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
Spring I/O 2012: Natural Templating in Spring MVC with ThymeleafThymeleaf
 
Thymeleafのすすめ
ThymeleafのすすめThymeleafのすすめ
Thymeleafのすすめeiryu
 
Spring User Group Thymeleaf 08-21-2013
Spring User Group Thymeleaf 08-21-2013Spring User Group Thymeleaf 08-21-2013
Spring User Group Thymeleaf 08-21-2013Justin Munn
 
DSpace UI Prototype Challenge: Spring Boot + Thymeleaf
DSpace UI Prototype Challenge: Spring Boot + ThymeleafDSpace UI Prototype Challenge: Spring Boot + Thymeleaf
DSpace UI Prototype Challenge: Spring Boot + ThymeleafTim Donohue
 
Modern Java web applications with Spring Boot and Thymeleaf
Modern Java web applications with Spring Boot and ThymeleafModern Java web applications with Spring Boot and Thymeleaf
Modern Java web applications with Spring Boot and ThymeleafLAY Leangsros
 
Introduction to Spring Boot!
Introduction to Spring Boot!Introduction to Spring Boot!
Introduction to Spring Boot!Jakub Kubrynski
 
REST with Spring Boot #jqfk
REST with Spring Boot #jqfkREST with Spring Boot #jqfk
REST with Spring Boot #jqfkToshiaki Maki
 
Spring Framework - MVC
Spring Framework - MVCSpring Framework - MVC
Spring Framework - MVCDzmitry Naskou
 
ORM, JPA, & Hibernate Overview
ORM, JPA, & Hibernate OverviewORM, JPA, & Hibernate Overview
ORM, JPA, & Hibernate OverviewBrett Meyer
 
Overview of JPA (Java Persistence API) v2.0
Overview of JPA (Java Persistence API) v2.0Overview of JPA (Java Persistence API) v2.0
Overview of JPA (Java Persistence API) v2.0Bryan Basham
 

Destacado (20)

Thymeleaf, will it blend?
Thymeleaf, will it blend?Thymeleaf, will it blend?
Thymeleaf, will it blend?
 
Introducing thymeleaf
Introducing thymeleafIntroducing thymeleaf
Introducing thymeleaf
 
Thymeleaf Introduction
Thymeleaf IntroductionThymeleaf Introduction
Thymeleaf Introduction
 
Spring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
Spring I/O 2012: Natural Templating in Spring MVC with ThymeleafSpring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
Spring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
 
Introduction to thymeleaf
Introduction to thymeleafIntroduction to thymeleaf
Introduction to thymeleaf
 
Thymeleafのすすめ
ThymeleafのすすめThymeleafのすすめ
Thymeleafのすすめ
 
Spring User Group Thymeleaf 08-21-2013
Spring User Group Thymeleaf 08-21-2013Spring User Group Thymeleaf 08-21-2013
Spring User Group Thymeleaf 08-21-2013
 
DSpace UI Prototype Challenge: Spring Boot + Thymeleaf
DSpace UI Prototype Challenge: Spring Boot + ThymeleafDSpace UI Prototype Challenge: Spring Boot + Thymeleaf
DSpace UI Prototype Challenge: Spring Boot + Thymeleaf
 
Java™ in Web 2.0
Java™ in Web 2.0Java™ in Web 2.0
Java™ in Web 2.0
 
Modern Java web applications with Spring Boot and Thymeleaf
Modern Java web applications with Spring Boot and ThymeleafModern Java web applications with Spring Boot and Thymeleaf
Modern Java web applications with Spring Boot and Thymeleaf
 
Introduction to jQuery
Introduction to jQueryIntroduction to jQuery
Introduction to jQuery
 
Introduction to spring boot
Introduction to spring bootIntroduction to spring boot
Introduction to spring boot
 
Introduction to JPA Framework
Introduction to JPA FrameworkIntroduction to JPA Framework
Introduction to JPA Framework
 
Introduction to Spring Boot!
Introduction to Spring Boot!Introduction to Spring Boot!
Introduction to Spring Boot!
 
REST with Spring Boot #jqfk
REST with Spring Boot #jqfkREST with Spring Boot #jqfk
REST with Spring Boot #jqfk
 
Spring Boot Tutorial
Spring Boot TutorialSpring Boot Tutorial
Spring Boot Tutorial
 
Spring Framework - MVC
Spring Framework - MVCSpring Framework - MVC
Spring Framework - MVC
 
JPA and Hibernate
JPA and HibernateJPA and Hibernate
JPA and Hibernate
 
ORM, JPA, & Hibernate Overview
ORM, JPA, & Hibernate OverviewORM, JPA, & Hibernate Overview
ORM, JPA, & Hibernate Overview
 
Overview of JPA (Java Persistence API) v2.0
Overview of JPA (Java Persistence API) v2.0Overview of JPA (Java Persistence API) v2.0
Overview of JPA (Java Persistence API) v2.0
 

Similar a Modern Java templating with Thymeleaf

Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From IusethisMarcus Ramberg
 
Let's write secure Drupal code! - DrupalCamp Oslo, 2018
Let's write secure Drupal code! - DrupalCamp Oslo, 2018Let's write secure Drupal code! - DrupalCamp Oslo, 2018
Let's write secure Drupal code! - DrupalCamp Oslo, 2018Balázs Tatár
 
GDI Seattle - Intro to JavaScript Class 4
GDI Seattle - Intro to JavaScript Class 4GDI Seattle - Intro to JavaScript Class 4
GDI Seattle - Intro to JavaScript Class 4Heather Rock
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologyDaniel Knell
 
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)James Titcumb
 
jQuery: Tips, tricks and hints for better development and Performance
jQuery: Tips, tricks and hints for better development and PerformancejQuery: Tips, tricks and hints for better development and Performance
jQuery: Tips, tricks and hints for better development and PerformanceJonas De Smet
 
Working with Javascript in Rails
Working with Javascript in RailsWorking with Javascript in Rails
Working with Javascript in RailsSeungkyun Nam
 
Let's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, Germany
Let's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, GermanyLet's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, Germany
Let's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, GermanyBalázs Tatár
 
Summer - The HTML5 Library for Java and Scala
Summer - The HTML5 Library for Java and ScalaSummer - The HTML5 Library for Java and Scala
Summer - The HTML5 Library for Java and Scalarostislav
 
Integrating SAP the Java EE Way - JBoss One Day talk 2012
Integrating SAP the Java EE Way - JBoss One Day talk 2012Integrating SAP the Java EE Way - JBoss One Day talk 2012
Integrating SAP the Java EE Way - JBoss One Day talk 2012hwilming
 
Idoc script beginner guide
Idoc script beginner guide Idoc script beginner guide
Idoc script beginner guide Vinay Kumar
 
Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)
Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)
Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)James Titcumb
 
Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosDivante
 
Rich Portlet Development in uPortal
Rich Portlet Development in uPortalRich Portlet Development in uPortal
Rich Portlet Development in uPortalJennifer Bourey
 

Similar a Modern Java templating with Thymeleaf (20)

Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From Iusethis
 
JS-05-Handlebars.ppt
JS-05-Handlebars.pptJS-05-Handlebars.ppt
JS-05-Handlebars.ppt
 
Let's write secure Drupal code! - DrupalCamp Oslo, 2018
Let's write secure Drupal code! - DrupalCamp Oslo, 2018Let's write secure Drupal code! - DrupalCamp Oslo, 2018
Let's write secure Drupal code! - DrupalCamp Oslo, 2018
 
Postman On Steroids
Postman On SteroidsPostman On Steroids
Postman On Steroids
 
Practica n° 7
Practica n° 7Practica n° 7
Practica n° 7
 
GDI Seattle - Intro to JavaScript Class 4
GDI Seattle - Intro to JavaScript Class 4GDI Seattle - Intro to JavaScript Class 4
GDI Seattle - Intro to JavaScript Class 4
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
 
jQuery: Tips, tricks and hints for better development and Performance
jQuery: Tips, tricks and hints for better development and PerformancejQuery: Tips, tricks and hints for better development and Performance
jQuery: Tips, tricks and hints for better development and Performance
 
Working with Javascript in Rails
Working with Javascript in RailsWorking with Javascript in Rails
Working with Javascript in Rails
 
Let's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, Germany
Let's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, GermanyLet's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, Germany
Let's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, Germany
 
Summer - The HTML5 Library for Java and Scala
Summer - The HTML5 Library for Java and ScalaSummer - The HTML5 Library for Java and Scala
Summer - The HTML5 Library for Java and Scala
 
Mojolicious
MojoliciousMojolicious
Mojolicious
 
Integrating SAP the Java EE Way - JBoss One Day talk 2012
Integrating SAP the Java EE Way - JBoss One Day talk 2012Integrating SAP the Java EE Way - JBoss One Day talk 2012
Integrating SAP the Java EE Way - JBoss One Day talk 2012
 
Idoc script beginner guide
Idoc script beginner guide Idoc script beginner guide
Idoc script beginner guide
 
Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)
Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)
Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)
 
Separation of concerns - DPC12
Separation of concerns - DPC12Separation of concerns - DPC12
Separation of concerns - DPC12
 
Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenarios
 
Rich Portlet Development in uPortal
Rich Portlet Development in uPortalRich Portlet Development in uPortal
Rich Portlet Development in uPortal
 

Último

GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfAlina Yurenko
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Natan Silnitsky
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsSafe Software
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceBrainSell Technologies
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...OnePlan Solutions
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfFerryKemperman
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalLionel Briand
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfDrew Moseley
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Mater
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentationvaddepallysandeep122
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringHironori Washizaki
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024StefanoLambiase
 
Software Coding for software engineering
Software Coding for software engineeringSoftware Coding for software engineering
Software Coding for software engineeringssuserb3a23b
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxTier1 app
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Hr365.us smith
 

Último (20)

GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data Streams
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. Salesforce
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdf
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive Goal
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdf
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentation
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort ServiceHot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their Engineering
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
 
Software Coding for software engineering
Software Coding for software engineeringSoftware Coding for software engineering
Software Coding for software engineering
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)
 

Modern Java templating with Thymeleaf

  • 1. A modern Java templating language Thymeleaf Andre Azzolini! Broadleaf Commerce! FW JUG - July 2nd, 2014
  • 2. Introduction Who am I?! ‣ Graduated from The University of Texas at Austin (Computer Science)! ‣ Consultant at Credera! ‣ Senior Engineer at Broadleaf Commerce! ! What is Broadleaf Commerce?! ‣ Open source Java enterprise eCommerce framework! ‣ Focus on extensibility and scalability! ‣ Based on Spring, Hibernate, Thymeleaf
  • 3. ‣ Why use Thymeleaf?! ‣ Thymeleaf Basics! ‣ Intermediate Thymeleaf! ‣ Existing Ecosystem! ‣ Broadleaf Use Cases Agenda
  • 4. ‣ Not compiled —> short feedback loop! ‣ Natural templating —> closer to designers’ HTML! ‣ Modular architecture —> hooks for customization Why use Thymeleaf?
  • 6. Installation in a Spring application <bean id="templateResolver" class="org.thymeleaf...ServletContextTemplateResolver"> <property name="prefix" value="/WEB-INF/templates/" /> <property name="suffix" value=".html" /> <property name="templateMode" value="HTML5" /> </bean> <bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine"> <property name="templateResolver" ref="templateResolver" /> </bean> ! <bean class="org.thymeleaf.spring3.view.ThymeleafViewResolver"> <property name="templateEngine" ref="templateEngine" /> <property name="order" value="1" /> <property name="viewNames" value="*.html" /> </bean>
  • 7. Outputting Values ———— HomeController.java ———— ! @RequestMapping("/") public String viewHomepage(Model model) { model.addAttribute("header", "My Header Message"); return "index"; } ! ———— /WEB-INF/templates/index.html ———— ! <span th:text="${header}">A Default Header</span> ! ———— Browser rendering ———— ! <span>A Default Header</span> ! ———— Thymeleaf rendering ———— ! <span>My Header Message</span>
  • 8. Outputting Values (i18n) ———— /WEB-INF/templates/index.html ———— ! <span th:text="#{homepage.header}">A Default Header</span> ! ———— messages_en.properties ———— ! homepage.header=My Header Message ! ———— messages_es.properties ———— ! homepage.header=Mi Mensaje de Cabecera ! ———— Thymeleaf rendering (en) ———— ! <span>My Header Message</span> ! ———— Thymeleaf rendering (es) ———— ! <span>Mi Mensaje de Cabecera</span>
  • 9. Outputting Values (i18n cont.) ———— HomeController.java ———— ! @RequestMapping("/") public String viewHomepage(Model model) { model.addAttribute("header", "homepage.header"); return "index"; } ! ———— /WEB-INF/templates/index.html ———— ! <span th:text="#{${header}}" />
  • 10. Outputting Raw Values ———— HomeController.java ———— ! @RequestMapping("/") public String viewHomepage(Model model) { model.addAttribute("header", "<b>My Bolded Header</b>"); return "index"; } ! ———— Rendered with th:text ———— ! <span>&lt;b&gt;My Bolded Header&lt;/b&gt;</span> ! ———— Rendered with th:utext ———— ! <span><b>My Bolded Header</b></span>
  • 11. Outputting Values Inline ———— index.html ———— ! <span th:inline="text"> [[${address.user?.firstName}]] [[${address.user?.lastName}]]<br /> [[${address.line1}]] <br /> [[${address.line2}]] <br /> [[${address.city}]], [[${address.state}]] [[${address.zipcode}]] </span>
  • 12. Scoping available variables ———— index.html ———— ! <span th:inline="text" th:object="${address}"> [[*{user?.firstName}]] [[${user?.lastName}]]<br /> [[*{line1}]] <br /> [[*{line2}]] <br /> [[*{city}]], [[*{state}]] [[*{zipcode}]] </span>
  • 13. Attribute Manipulation ———— index.html ———— ! <span th:class="${isEven ? 'even' : 'odd'}" th:classappend="${isError ? 'error'}" th:attr="data-row-id=${row.id}" th:text="${row.description}" /> ———— Rendered index.html ———— ! <span class="odd error" data-row-id="5">Row 5 Description</span>
  • 14. Conditionals ———— index.html ———— ! <span th:if="${user.loggedIn}" th:text="${'Hi ' + user.name}" /> <span th:unless="${user.loggedIn}">Welcome guest</span> ! ———— messages_en.properties ———— ! header.greeting.user=Hi {0} header.greeting.anonymous=Welcome guest ! ———— index.html (i18n) ———— ! <span th:if="…" th:text="#{header.greeting.user(${user.name})}" /> <span th:unless="…" th:text="#{header.greeting.anonymous}" />
  • 15. Loops and Links ———— HomeController.java ———— ! @RequestMapping("/products") public String viewProductListing(Model model) { List<Product> products = productService.findAll(); model.addAttribute("products", products); return "productListing"; } ! ———— productListing.html ———— ! <ul> <li th:each="product : ${products}" th:object="${product}"> <img th:src="@{*{mainImage}}" /> <br /> <a th:href="@{*{link}}" th:text="*{name}" /> </li> </ul>
  • 16. Includes ———— productListing.html ———— ! <ul> <li th:each="product : ${products}" th:object="${product}" th:include="components/productBlock" /> </ul> ! ———— components/productBlock.html ————
 ! <img th:src="@{*{mainImage}}" /> <br /> <a th:href="@{*{link}}" th:text="*{name}" />
  • 17. Variable Expressions ———— HomeController.java ———— ! @RequestMapping("/products") public String viewProductListing(Model model) { List<Product> products = productService.findAll(); model.addAttribute("products", products); return "productListing"; } ! ———— productListing.html ———— ! <span th:text="${'Products Found: ' + #lists.size(products)}" />
  • 18. ‣ format(date, 'dd/MMM/yyyy HH:mm')! ‣ day(date), month(date), dayOfWeek(date), etc! ‣ create(year, month, day)! ‣ createNow(), createToday() #dates Variable Expression
  • 19. ‣ formatInteger(num, 3, 'POINT')! ‣ sequence(from, to)! ‣ sequence(from, to, step) #numbers Variable Expression
  • 20. ‣ isEmpty! ‣ contains! ‣ indexOf, substring, replace! ‣ prepend, append! ‣ toLowerCase, toUpperCase, capitalize, capitalizeWords! ‣ escapeXml, unescapeJava #strings Variable Expression
  • 21. ‣ isEmpty, size! ‣ contains, containsAll! ‣ sort #arrays, #lists, #sets, #maps Variable Expressions
  • 22. #aggregates Variable Expression ———— Order.java ———— ! public class Order { protected List<OrderLine> lines; } ! class OrderLine { protected BigDecimal price; protected int qty; } ! ———— order.html ———— ! <span th:text="${#aggregates.sum(order.lines.{price * qty})}" />
  • 24. Custom Variable Expressions ———— SystemPropertyVariableExpression.java ———— ! @Resource protected SystemPropertyService service; ! public String getName() { return "sp"; } ! public boolean getAsBoolean(String prop) { return service.resolveAsBoolean(prop); } ! ———— index.html ———— ! <span th:if="${#sp.getAsBoolean('pagination.enabled')}" th:include="components/paginator" />
  • 25. Spring Bean Direct Access ———— MyService.java ———— ! @Service public class MyService { ! public boolean isPalindrome(String str) { return str.equals(StringUtils.reverse(str)); } ! } ! ———— index.html ———— ! <span th:if="${@myService.isPalindrome('tacocat')}" />
  • 26. Custom Processors ———— PriceProcessor.java ———— ! public class PriceProcessor extends AbstractTextChildModifierAttrProcessor { public PriceTextDisplayProcessor() { super("price"); } ! protected String getText(Arguments arguments, Element element, String attributeName) { Expression expression = ... Object result = expression.execute(...); if (result instanceof Money) { return ((Money) result).getFormattedValue(); } else { return result.toString(); } } }
  • 27. Custom Processors (cont.) ———— MyCustomDialect.java ———— public class MyCustomDialect extends AbstractDialect { private Set<IProcessor> processors = new HashSet<IProcessor>(); @Override public String getPrefix() { return "mcd"; } } ! ———— applicationContext.xml ———— ! <bean id="myCustomDialect" class="com.myco.MyCustomDialect"> <property name="processors"> <set> <bean id="priceProcessor" class="com.myco.PriceProcessor"> </set> </property> </bean>
  • 28. Custom Processors (cont.) ———— applicationContext.xml (cont.) ———— ! <bean id="templateEngine" class="..."> <property name="additionalDialects"> <set> <bean class="com.myco.MyCustomDialect"/> </set> </property> </bean> ! ———— components/productBlock.html ————
 ! <img th:src="@{*{mainImage}}" /> <br /> <a th:href="@{*{link}}" th:text="*{name}" /> <span mcd:price="*{price}" />
  • 29. Custom Processors (cont.) public class FormProcessor extends AbstractElementProcessor { ! protected ProcessorResult processElement(Arguments a, Element el) { String method = el.getAttributeValue("method"); if (!"GET".equals(method)) { String csrfToken = protectionService.getCsrfToken(); Element csrfNode = new Element("input"); csrfNode.setAttribute("type", "hidden"); csrfNode.setAttribute("name", "csrf-token"); csrfNode.setAttribute("value", csrfToken); el.addChild(csrfNode); } ! Element newForm = el.cloneElementNode(...); el.getParent().insertAfter(el, newForm); el.getParent().removeChild(el); return ProcessorResult.OK; } }
  • 30. Custom Processors (cont.) ———— login.html ———— ! <mcd:form> <input type="text" name="username" /> <input type="password" name="pass" /> </mcd:form> ! ———— Rendered login page ———— ! <form> <input type="text" name="username" /> <input type="password" name="pass" /> <input type="hidden" name="csrf-token" value="L9ThxnotKPzthJ" /> </form>
  • 31. Spring Form Binding ———— UserRegistrationForm.java ———— ! public class UserRegistrationForm { ! protected String username; protected String password; protected String confirmPassword; protected String email; ! ... getters / setters ... ! }
  • 32. Spring Form Binding (cont.) ———— UserRegistrationController.java ———— ! public class UserRegistrationController { ! @RequsetMapping("/register", method = RequestMethod.GET) public String showRegisterForm(Model model, @ModelAttribute UserRegistrationForm registerForm) { return "components/userRegistrationForm"; } ! @RequsetMapping("/register", method = RequestMethod.POST) public String showRegisterForm(Model model, @ModelAttribute UserRegistrationForm registerForm) { // register the user return "redirect:/"; } ! }
  • 33. Spring Form Binding (cont.) ———— components/userRegistrationForm.html ———— ! <form th:action="@{/register}" th:object="${registerForm}" method="POST"> <input type="text" th:field="*{username}" /> <input type="password" th:field="*{password}" /> <input type="password" th:field="*{confirmPassword}" /> <input type="text" th:field="*{email}" /> </form>
  • 35. ‣ Code completion for out of box processors! ‣ Content assist inside expressions! ‣ Ability to provide completion for custom processors Eclipse IDE Plugin
  • 36. ‣ Use Thymeleaf templates in Tiles definitions! ‣ Mix JSP and Thymeleaf templates! ‣ Optional Spring MVC 3 and Spring Web Flow 2.3 integrations Thymeleaf + Apache Tiles 2
  • 37. ‣ Lightweight dialect approach instead of Tiles! ‣ Uses decorators and fragments within the templates, so there is no need for a Tiles definition file! ‣ Created by a core Thymeleaf contributor! ‣ I think it's more intuitive than the Tiles plugin Layout Dialect (unofficial)
  • 38. ‣ sec:authorize processor (accepts normal Spring Security expressions like hasRole('ROLE_ADMIN'))! ‣ Grab the current authentication object in expressions: ${#authentication.name}! Spring Security 3
  • 39. ‣ Allows you to specify a cache attribute on a DOM element! ‣ Caches the resulting HTML with the provided name! ‣ Doesn't require Thymeleaf to process the DOM for that node when rendering Cache Dialect (unofficial) <ul cache:name="productsList" cache:ttl="60"> <li th:each="product : ${products}" th:include="components/productBlock" /> </ul>
  • 40. ‣ JavaScript library for natural templating! ‣ Can process th:include without executing in an application! ‣ Provides features for evaluating conditionals while statically prototyping Thymol (unofficial)
  • 42. ‣ Serve individual files in development, but a bundle in production! ‣ Handle expiration of bundles! ‣ Dynamically modify contents of certain resources JS / CSS Bundling <blc:bundle name="admin.js" files="BLC.js, BLC-system-property.js, blc-dates.js" />
  • 43. ‣ Intelligent auto-generation of cache keys! ‣ Provide ability to alter a portion of the cached, generated HTML! ‣ Invalidate cache elements when things change in the admin Advanced Caching Strategy
  • 44. ‣ Allow users to modify templates through the admin tool! ‣ Serve templates directly from the database! ‣ No need for compiling the templates like we would have to with JSP Database Template Resolution