Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

Broadleaf Presents Thymeleaf

4 924 vues

Publié le

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

Publié dans : Logiciels, Technologie, Formation
  • Login to see the comments

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!

×