Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Broadleaf Presents Thymeleaf

4,904 views

Published on

Broadleaf's Andre Azzolini presents Thymeleaf at July 2014 Fort Worth Java Users Group (FW JUG) meeting.

Published in: Software, Technology, Education
  • Was a little hesitant about using ⇒⇒⇒WRITE-MY-PAPER.net ⇐⇐⇐ at first, but am very happy that I did. The writer was able to write my paper by the deadline and it was very well written. So guys don’t hesitate to use it.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • If we are speaking about saving time and money this site ⇒ www.HelpWriting.net ⇐ is going to be the best option!! I personally used lots of times and remain highly satisfied.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Doctors Predicted I Would NEVER Stop Snoring But Contrarily to their Prediction, I Did It 100% Naturally! learn more...  http://t.cn/Aigi9dEf
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • I'm sleeping so much better! Hi David, just a quick message to say thank you so much for this. I have been using a CPAP machine for 3 years and I absolutely hate it. It's so uncomfortable and I sleep worse with it on than I do without it. I'm now sleeping much better thanks to your program. And my wife is so much happier too! ♣♣♣ http://t.cn/Aigi9dEf
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • You rock!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Broadleaf Presents Thymeleaf

  1. 1. A modern Java templating language Thymeleaf Andre Azzolini! Broadleaf Commerce! FW JUG - July 2nd, 2014
  2. 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. 3. ‣ Why use Thymeleaf?! ‣ Thymeleaf Basics! ‣ Intermediate Thymeleaf! ‣ Existing Ecosystem! ‣ Broadleaf Use Cases Agenda
  4. 4. ‣ Not compiled —> short feedback loop! ‣ Natural templating —> closer to designers’ HTML! ‣ Modular architecture —> hooks for customization Why use Thymeleaf?
  5. 5. Thymeleaf Basics
  6. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 19. ‣ formatInteger(num, 3, 'POINT')! ‣ sequence(from, to)! ‣ sequence(from, to, step) #numbers Variable Expression
  20. 20. ‣ isEmpty! ‣ contains! ‣ indexOf, substring, replace! ‣ prepend, append! ‣ toLowerCase, toUpperCase, capitalize, capitalizeWords! ‣ escapeXml, unescapeJava #strings Variable Expression
  21. 21. ‣ isEmpty, size! ‣ contains, containsAll! ‣ sort #arrays, #lists, #sets, #maps Variable Expressions
  22. 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})}" />
  23. 23. Intermediate Thymeleaf
  24. 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. 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. 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. 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. 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. 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. 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. 31. Spring Form Binding ———— UserRegistrationForm.java ———— ! public class UserRegistrationForm { ! protected String username; protected String password; protected String confirmPassword; protected String email; ! ... getters / setters ... ! }
  32. 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. 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>
  34. 34. Existing Ecosystem
  35. 35. ‣ Code completion for out of box processors! ‣ Content assist inside expressions! ‣ Ability to provide completion for custom processors Eclipse IDE Plugin
  36. 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. 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. 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. 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. 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)
  41. 41. Broadleaf Use Cases
  42. 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. 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. 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
  45. 45. Thanks!

×