javathymeleafspring-thymeleaf

Is there a way to call utility method in TEXT Thymeleaf template?


I have application aimed to transform API responses into human-readable text. For that purpose I call data from 3rd parties, build the model from it and run template against context through org.thymeleaf.TemplateEngine.

Example:

public Optional<String> process(String template, Map<String, Object> model) {
        Context thymeleafContext = new Context();
        thymeleafContext.setVariables(model);

        try {
            return Optional.of(templateEngine.process(template, thymeleafContext););
        } catch (TemplateProcessingException e) {
            log.error(e.getMessage());
        }

        return Optional.empty();
    }

For that purpose I have a number of plaintext Thymeleaf templates org.thymeleaf.templatemode.TemplateMode#TEXT (keep note this) placed into separate .txt files.

Let's assume I have template like this: Born year: [[${bornYear}}]]. Now I need to cast the value of bornYear variable into specific pattern. For that purpose I have utility method like this

public class Utils {

    private Utils() {}

    public static String cast(String source, String pattern) {
        return "<implement me>";
    }
}

and I assume I can execute this utility method in the template. However I can not make it possible, at least I get exception on template processing.

What I tried:

TemplateResolver configuration:

@Bean
public ClassLoaderTemplateResolver templateResolve() {
    ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
    templateResolver.setPrefix("templates/");
    templateResolver.setSuffix(".txt");
    templateResolver.setTemplateMode(TemplateMode.TEXT);
    templateResolver.setCharacterEncoding("UTF-8");
    templateResolver.setOrder(1);
    templateResolver.setCheckExistence(true);

    return templateResolver;
}

My questions are:

  1. Is that possible to call custom code considering I do use TemplateMode.TEXT?
  2. If yes, how to do it in a right way?
  3. Any suggestions?

Thank you in advance!


Solution

  • The way I would do this is...

    1. Change utils to be a regular object:

       public class Utils {
           public String cast(String source, String pattern) {
               return "<implement me>";
           }
       }
      
    2. Always put a utils onto your model:

       public Optional<String> process(String template, Map<String, Object> model) {
           Context thymeleafContext = new Context();
           model.put("utils", new Utils());
           thymeleafContext.setVariables(model);
      
    3. In your template, use it like this:

      Born year: [[${utils.cast(bornYear, 'dd-MM-yyyy')}]]
      

    Another way you can do this, when instantiating your context add a ThymeleafEvaluationContext like this. You will have to get access to your Spring ApplicationContext as well.

    public Optional<String> process(String template, Map<String, Object> model) {
        Context thymeleafContext = new Context();
        context.setVariable(ThymeleafEvaluationContext.THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME, new ThymeleafEvaluationContext(applicationContext, null));
        thymeleafContext.setVariables(model);
    

    Then if you've annotated your utils class with @Component and made sure it is in a package that is being scanned by your configuration:

    @Component
    public class Utils {
        public String cast(String source, String pattern) {
            return "<implement me>";
        }
    }
    

    You will be able to access Beans in your templates like this:

    Born year: [[${@utils.cast(bornYear, 'dd-MM-yyyy')}]]