Template Extension Methods

Template Extension Methods

Template extension methods are used to extend the set of accessible properties of data objects.  

Sometimes, you’re not in control of the classes that you want to use in your template, and you cannot add methods to them. Template extension methods allow you to declare new methods for those classes that will be available from your templates just as if they belonged to the target class.

 

Let’s keep extending on our simple HTML page that contains the item name and price to add a discounted price. The discounted price in this example is sometimes called a "computed property". You'll implement a template extension method to render this property easily.

 

The first step is to update the src/main/resources/templates/ItemResource/item.html file by replacing its existing contents with the following:
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>{item.name}</title>
    </head>
    <body>
        <h1>{item.name}</h1>
        <div>Price: {item.price}</div>
        {#if item.price > 100}
        <div>Discounted Price: {item.discountedPrice}</div>
        {/if}
    </body>
</html>

 

Notice the use of Handlebar-esque {#if ...} block. It's part of Qute's basic control flow features.

 

Also notice the use of {item.discountedPrice}. This field does not exist in the Item class (which only has name and price). You'll add an extension to the Java code to make this property available to the template, as well as write the code that computes its value in Java.

 

Create a new file at src/main/java/org/acme/qute/TemplateExtensions.java and enter the following code:
package org.acme.qute;

import java.math.BigDecimal;
import io.quarkus.qute.TemplateExtension;

@TemplateExtension
public class TemplateExtensions {

    public static BigDecimal discountedPrice(Item item) {
        return item.price.multiply(new BigDecimal("0.9"));
    }
}
Here we declare a static template extension method that can be used to add "computed properties" to a data class. The class of the first parameter (in this case Item) is used to match the base object and the method name is used to match the property name (in this case discountedPrice). When the template accesses {item.computedPrice}, the contextual value of item is passed to this discountedPrice method to compute its value (in this case, using item.price.multiply() to apply a 10% discount by multiplying by 0.9).
Note: You can place template extension methods in every class if you annotate them with @TemplateExtension, but the convention is to keep them either grouped by target type or in a single class named TemplateExtensions.
Run the previous URL which returns the price of apples:
curl http://localhost:8080/item/1

Since the price of apples is less than $100 (the condition in the template), the discount is not applied. Now try with an item (mangos) that will trigger the discount:

curl http://localhost:8080/item/4

You should see:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>Mango</title>
    </head>
    <body>
        <h1>Mango</h1>
        <div>Price: 129.990000000000009094947017729282379150390625</div>
        <div>Discounted Price: 116.9910000000000081854523159563541412353515625</div>
    </body>
</html>

 

Notice the inclusion of "Discounted Price", which demonstrates both the conditionals in the template and the template extension for calculating it.