Skip to content

anosim114/qute-spring-boot-starter

Repository files navigation

Build Maven Central

Logo

Qute Spring Boot Starter

Qute is primarily designed as a Quarkus extension. It is possible to use it as a "standalone" library too. However, in such case some of the features are not available. In general, any feature mentioned under the Quarkus Integration section is missing. You can find more information about the limitations and possibilities in the Qute Used as a Standalone Library section.

Sure thing, here we go!

This project is in its core a port of the Quarkus Qute configuration to Spring Boot enabling various Spring specific features to be used instead of Quarkus's. It demonstrates how to set up and use Qute in a Spring Boot application, offering a better alternative to other templating engines like for example Thymeleaf.

{@String name = 'World'}

{#include layout}

  {#title}A Qute Readme{/title}
  
  {#content}
    <h1>
        Hello {name}!
    </h1>
    
    <p>{msg:t('readme.welcome-message')}</p>
  {/content}
  
{/include}

Why you should use Qute as a templating language

  • Qute supports implementations of component libraries (see example-component-lib and example-backend as reference implementation)
  • Qute natively supports slotting (unlike thymeleaf)
  • Qute has a pleasant syntax (unlike thymeleaf)
  • Qute supports improved developer-experience (ex. hot-reloading capabilities)
  • Qute is backed by major corporations (whoever works on Quarkus)
  • Qute is actively maintained (unlike thymeleaf)

For a rough overview of Qute's features read the Qute Reference Guide

Usage

Add required dependency

<dependency>
    <groupId>net.snemeis</groupId>
    <artifactId>qute-spring-boot-starter</artifactId>
    <version>${version.qute-starter}</version>
</dependency>

The starter then autoconfigures a Qute engine instance and a template ViewResolver.

Using the ViewResolver

{!/templates/index.qute.html!}
{@java.lang.String name}

Hello {name}!
// MainController.java

@GetMapping("/") 
public String index(Model model) {
    model.addAttribute("name", "World");
    return "index";
}

Using the Qute engine directly

Alternatively to resolving templates using the ViewResolver the Engine instance can also be accessed directly by calling Qute.engine().
One notable difference is that the template cache is not being cleared when the cache is turned off via spring.qute.cache-enabled=false.

@GetMapping("/") 
@ResponseBody
public String index() {
    return Qute.engine()
        .getTemplate("index")
        .data(Map.of("name", "World"))
        .render();
}

// or

@GetMapping("/") 
@ResponseBody
public String index() {
    return Qute.fmt('Hello {name}!')
        .data(Map.of("name", "World"))
        .render();
}

Configuration

For all configuration options of this starter take a look at the qute properties file.

For the quarkus configuration and their explanations: Qute Reference Guide 4.15. Configuration Reference. (The ones for this starter are leaned on the original implementation).

Example configuration for local development:

# to load templates from filepath instead of classpath (direct editing feedback)
# good if coupled with something like:
# spring.resources.static-locations=file:///${user.dir}/src/main/resources/static/
spring.qute.dev-mode = true
# set path to filepath
spring.qute.dev-prefix = ${user.dir}/src/main/resources/templates/
# for hot-reloading in dev-mode
# (tip: good when coupled with custom vite config to force page reload on file save
# https://github.com/ElMassimo/vite-plugin-full-reload, see laravel using this for example)
spring.qute.caching-enabled = false
# to be able to resolve filenames like 'index', 'index.html' or 'index.qute.html'
spring.qute.suffixes = ,.html,.qute.html
# to get parsing mistakes in the templates
logging.level.io.quarkus=DEBUG

Adding value and namespace resolvers

Beans of type ValueResolver and NamespaceResolver are collected and registered as resolvers on initialization. (As well as ParserHooks because the original implementation had this as well)

Example:

@Configuration
class ValueResolverConfig {
  @Bean
  ValueResolver valueResolverA() {
      return new ValueResolverA();
  }
}

Adding Template Extensions (or helper methods in a RoR sense)

Like in the quarkus qute reference guide described, classes annotated with @TemplateExtension are collected at startup and registered. Current rules are that extension methods must be static and only the bare annotation is supported, so no annotation arguments.

@TemplateExtension
class StringExtensions {
  public static String capitalize(String str) {
    return str.toUpperCase();
  }
}
{@java.lang.String humblebeeSounds} <!-- 'bzzzzzzzzzz' -->
The humble-bee goes: "{humblebeeSounds.capitalize}". <!-- transformed to 'BZZZZZZZZZZ' -->

Adding output transformation logic

A feature inspired from Vite, is the possibility to transform the output of a rendering. For this purpose it's possible to implement a bean which implements the TemplatePostProcessor interface.

With this it's possible to state a condition when the transformation should happen and the logic for the transformation itself.

@Component
class VersionTransformation implements TemplatePostProcessor {
    @Overrides
    Boolean appliesTo(HttpServletRequest req) {
      // default implementation is -> true
      return Objects.nonNull(request.getAttribute("doTransform"));
    }
    
    @Overrides
    String process(String renderedTemplate) {
        return renderedTemplate.replaceAll("VERSION", "1.0.0");
    }
}

With this all templates which fulfill the condition will have the string "VERSION" globally replaced to a version string number.

Html helpers

<!-- adding normal attributes -->
<div {html:attr("id", "some-id")} {html:attr(someKey, someVar)}></div>
<!-- => <div id="some-id" somekey="somevar"></div> -->

<!-- adding classes -->
<div {html:class("some static classes", someVar1, someVar2)}></div>
<!-- => <div class="some static classes var1 var2"></div> -->

<!-- non values will be omitted -->
<div {html:class("some static classes", nullVar, someVar)}></div>
<!-- => <div id="some static classes some-var"></div> -->
<div {html:attr("someKey", nullVar)}></div>
<!-- => <div></div> -->

Reference

For a general reference see the Qute Reference Guide.

The spring related changes are the following.

Qute Reference Guide 4.3. Injecting Beans Directly In Templates

Since Spring Boot also has beans which can be referenced at runtime, this feature is also supported via the cdi and inject namespaces. So the following snippet is 100% valid:

{cdi:someService.findSomeValue(10).name} 
{inject:otherService.fetchSomethingElse()}

Qute Reference Guide 4.14. Type-safe Message Bundles

This starter does not support type-safe message bundles, however some part of the syntax to retrieve messages from a message bundle is still valid:

{msg:t('some-key', 'with', 'params', 'for the message')}
{msg:t('some-other-key', oneVariable, 42)}

About

Qute template engine starter for Spring Boot projects

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •