springspring-mvcthymeleaftiles2

Thymeleaf 3 and Tiles2 integration


Does Thymeleaf 3 support Tiles 2 somehow? There is a package that I was using for Thumeleaf 2.x.x thymeleaf-extras-tiles2-spring4 but as I see now it is not compatible because of changes in org.thymeleaf.dialect.AbstractDialect class

Caused by: java.lang.NoSuchMethodError: org.thymeleaf.dialect.AbstractDialect: method <init>()V not found
[INFO]  at org.thymeleaf.extras.tiles2.dialect.TilesDialect.<init>(TilesDialect.java:46)

Do I need to wait for an update of this integration to be able to start with T3?

Is there any way that I can simulate Tiles in Thymeleaf3

I only use my Tiles for something like this:

  <definition name="portal/**" template="layouts/portal">
    <put-attribute name="_head" value="/portal/{1} :: _head"/>
    <put-attribute name="content" value="/portal/{1} :: content"/>
  </definition>

Solution

  • To solve that problem I have created a proxy for SpringTemplateEngine and advisedTemplateEngine.process() method.

    How it works:

    The code map layouts on the basis of template path for instance:

    portal/moje_konto/moje_dane
    

    is mapped to layout

     LAYOUTS_PATH/portal
    

    Additionally, it passes variable VIEW containing a path to actual template

    Inside layouts it may be used to include specific fragments. Very simple portal layout may look like that:

    <!DOCTYPE html SYSTEM "about:legacy-compat">
    <html lang="pl" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.springframework.org/security/tags">
        <body>
           <div th:replace="${'portal/' + VIEW} :: content">Content</div>
        </body>
    </html>
    

    Controller:

    @PreAuthorize("isAuthenticated()")
      @RequestMapping(value = "/", method = RequestMethod.GET)
      public String home(Model model) {
    
        return "portal/home/index";
      }
    

    TemplateEngine:

    public class LayoutTemplateEngine implements ITemplateEngine, MessageSourceAware, InitializingBean {
    
      private final Logger logger = Logger.getLogger(this.getClass().getName());
    
      private final String LAYOUTS_PATH = "layouts/";
    
      private final SpringTemplateEngine templateEngine = new SpringTemplateEngine();
    
      @Override
      public void process(TemplateSpec templateSpec, IContext context, Writer writer) {
        String template = templateSpec.getTemplate();
    
        logger.info("Rendering template: " + template);
    
        if (context instanceof WebExpressionContext) {
          int end = template.indexOf("/");
          if (end != -1) {
            // change template
            templateSpec = new TemplateSpec(LAYOUTS_PATH + template.substring(0, end), templateSpec.getTemplateSelectors(), templateSpec.getTemplateMode(), templateSpec.getTemplateResolutionAttributes());
            // add VIEW variable
            ((WebExpressionContext)context).setVariable("VIEW", template.substring(end + 1));
          }
        }
    
        templateEngine.process(templateSpec, context, writer);
        logger.info("Rendering finished");
      }
    
      public void setTemplateResolver(final ITemplateResolver templateResolver) {
        templateEngine.setTemplateResolver(templateResolver);
      }
    
      public void setEnableSpringELCompiler(final boolean enableSpringELCompiler) {
        templateEngine.setEnableSpringELCompiler(enableSpringELCompiler);
      }
    
      public void addDialect(final IDialect dialect) {
        templateEngine.addDialect(dialect);
      }
    
      public void addTemplateResolver(final ITemplateResolver templateResolver) {
        templateEngine.addTemplateResolver(templateResolver);
      }
    
      @Override
      public IEngineConfiguration getConfiguration() {
        return templateEngine.getConfiguration();
      }
    
      @Override
      public String process(String template, IContext context) {
        return process(new TemplateSpec(template, null, null, null), context);
      }
    
      @Override
      public String process(String template, Set<String> templateSelectors, IContext context) {
        return process(new TemplateSpec(template, templateSelectors, null, null), context);
      }
    
      @SuppressWarnings("resource")
      @Override
      public String process(TemplateSpec templateSpec, IContext context) {
        final Writer stringWriter = new FastStringWriter(100);
        process(templateSpec, context, stringWriter);
        return stringWriter.toString();
      }
    
      @Override
      public void process(String template, IContext context, Writer writer) {
        process(new TemplateSpec(template, null, null, null), context, writer);
      }
    
      @Override
      public void process(String template, Set<String> templateSelectors, IContext context, Writer writer) {
        process(new TemplateSpec(template, templateSelectors, null, null), context, writer);
      }
    
      @Override
      public IThrottledTemplateProcessor processThrottled(String template, IContext context) {
        return processThrottled(new TemplateSpec(template, null, null, null), context);
      }
    
      @Override
      public IThrottledTemplateProcessor processThrottled(String template, Set<String> templateSelectors, IContext context) {
        return processThrottled(new TemplateSpec(template, templateSelectors, null, null), context);
      }
    
      @Override
      public IThrottledTemplateProcessor processThrottled(TemplateSpec templateSpec, IContext context) {
        return templateEngine.processThrottled(templateSpec, context);
      }
    
      @Override
      public void afterPropertiesSet() throws Exception {
        templateEngine.afterPropertiesSet();
      }
    
      @Override
      public void setMessageSource(MessageSource messageSource) {
        templateEngine.setMessageSource(messageSource);
      }
    }