Qute is a templating engine designed specifically for Quarkus. If you have been using JSF/JSTL you will find some concepts similar, although Qute can be used to produce a Front-End for your applications combining both the imperative and the non-blocking reactive style of coding plus validation of expressions referenced. Let's get into it!

To get started, you need to include the following extension in your application so to use Qute in combination with JAX-RS:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy-qute</artifactId>
</dependency>

In order to use Qute, we will be placing templates into the folder src/main/resources/templates where they are automatically picked up by Quarkus. In a nutshell, a template can be as simple as the following hello.html file:

Good morning {name}!

{name} is a value expression that is evaluated when the template is rendered and will be resolved against the current context object. By default, the current context object represents the data passed to the template instance. Let's see how to build it with an example:

The following Endpoint, references a Template named "time" and passes the "zone" attribute to build the current context object:

package com.sample;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateInstance;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

@Path("/time")
public class TimeResource {

    // Matches with time.html
    @Inject
    Template time;

    @GET
    @Produces(MediaType.TEXT_HTML)
    public TemplateInstance get(@QueryParam("zoneId") String zoneId) {
        Date date = null;
        try {
            LocalDateTime localDateTime = LocalDateTime.now(ZoneId.of(zoneId));
            date = Date.from( localDateTime.atZone( ZoneId.systemDefault()).toInstant());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return time.data("zone",date);
    }

}

As a result, the template "time" will have access to data which has been added to "zone". This data is your LocalDateTime, or if you provided a valid ZoneId, the LocalDateTime according to that time. Plain and simple. Now let's check the src/resources/templates/time.html page:

{! This is a comment !}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">

</head>
<body>
<h2>Quarkus Qute demo</h2>

    {#if zone != null}
    <p>Time now: {zone}</p>
    {#else}
    <p>Time now: {inject:time.defaultTime}</p>
    {/if}


    {#include footer}

    {/include}

</body>
</html>

The page begins with a comment section:

{! This is a comment !}

Then, it contains a conditional execution, using the {#if} {#else} {/if} block. So, if the engine contains the "zone" data it will print it. Otherwise we will print an information contained in the property "defaultTime" CDI Bean named as "time":

package com.sample;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import java.util.Date;

@ApplicationScoped
@Named
public class Time {
    public Date defaultTime = new Date();;
    
    public Time() {

    }

    public Date getDefaultTime() {
        return defaultTime;
    }

    public void setDefaultTime(Date defaultTime) {
        this.defaultTime = defaultTime;
    }
}

So this is another way to resolve expressions in Qute: by injecting data directly from @Named CDI Beans, pretty much the same as JSF framework does.

The last section in our template is used to include another template, in a similar way to the old JSP include directive:

    {#include footer}

    {/include}

The footer.html template will be included. It is a simple HTML page with some info on how to query for different time zones:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
This is your local time.
You can query for additional times as follows: <a href="/time?zoneId=GMT">http://localhost:8080/time?zoneId=GMT</a>
<br/>
<a href="/help">Help</a>

</html>

Here is our template in action:

Qute quarkus tutorial Qute quarkus tutorial

At the bottom of this template, we have linked the "helper" template which introduces our second example, in which we will show how to iterate over a set of data using the Qute template.

Let's check how the helper Template works, starting from the HelperResource Endpoint:

package com.sample;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateExtension;
import io.quarkus.qute.TemplateInstance;

@Path("help")
public class HelperResource {

    @Inject
    Template helper;

    @GET
    @Produces(MediaType.TEXT_HTML)
    public TemplateInstance get() {
        List<TimeFormat> data = new ArrayList<>();
        data.add(new TimeFormat("UTC", "Western European Time"));
        data.add(new TimeFormat("UT", "Universal Time"));
        data.add(new TimeFormat("GMT", "Greenwich Time"));
        return helper.data("data", data);
    }


    @TemplateExtension
    static String wiki(TimeFormat  data) {
        return "https://en.wikipedia.org/w/index.php?search="+data.getName()+"&fulltext=Search";
    }

}

This template pushes a list of TimeFormat Objects into the helper template, with some information about the Time Codes. Additionally, there is another Qute gotcha here, that is the extension. What is it? in short, a Qute extension allows us to extend the data provided to the template with new functionality. So in our case, let's suppose we cannot alter the content of the TimeFormat but we need to push an extra field into the template data. Extension is the answer! In our case, we will push as extra field a link to a wikipedia search for that field.

Now it's time to check the helper.html template:

<body>
    <h2>Available data formats</h2>

    <table class="blueTable">
        <thead>
        <tr>

            <th>Name</th>
            <th>Description</th>
            <th>Wiki link</th>
        </tr>
        </thead>
        <tbody>
        {#for item in data}
        <tr>
            <td>{item.name}</td>
            <td>{item.description}</td>
            <td><a href="/{item.wiki}">{item.wiki}</a></td>
        </tr>
        {/for}
        </tbody>
    </table>
</body>

In this template we show you how to iterate through the Template data, in a similar way to how we used to do with JSTL. So, once we have captured the "data" from the TemplateInstance, we iterate on its content. The name and description field derive from the TimeFormat class (which follows here) and the "wiki" field derives

public class TimeFormat {
    String name;
    String description;

    public TimeFormat(String name, String description) {
        this.name = name;
        this.description = description;
    }

}

Here is how the helper template looks like:

Qute quarkus tutorial Qute quarkus tutorial

Conclusion

In this tutorial we have learned how to use Qute as template engine for Quarkus. We have some basic examples of solving context expressions and more advanced examples using extensions.

You can find the full code for this tutorial here: https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/front-end/qute

0
0
0
s2sdefault