Template Parameter Declarations
If you declare a parameter declaration in a template, Qute will attempt to validate all expressions that reference this parameter. If an incorrect expression is found the build will fail. This can greatly reduce developer errors upfront. Let's exercise this.
src/main/java/org/acme/Item.java
and enter in the body:
package org.acme;
import java.math.BigDecimal;
public class Item {
public String name;
public BigDecimal price;
public Item(BigDecimal price, String name) {
this.price = price;
this.name = name;
}
}
Creating the Template
mkdir -p src/main/resources/templates/ItemResource
Create a Qute template in that directory named item.html
and enter the following body:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>{item.name}</title>
</head>
<body>
<h1>{item.name}</h1>
<div>Price: {item.price}</div>
</body>
</html>
Creating the Service
The next step is to create a simple service to mock up a database of items. Create a new file at src/main/java/org/acme/ItemService.java
and enter the following code:
package org.acme;
import java.math.BigDecimal;
import java.util.Map;
import javax.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class ItemService {
private Map<Integer, Item> items = Map.of(
1, new Item(new BigDecimal(1.99), "Apple"),
2, new Item(new BigDecimal(2.99), "Pear"),
3, new Item(new BigDecimal(3.99), "Grape"),
4, new Item(new BigDecimal(129.99), "Mango")
);
public Item findItem(int id) {
return items.get(id);
}
}
Creating the REST Endpoint
The last step is to create the REST endpoint to access the service. This implementation will use a type-safe template. Create a new file at src/main/java/org/acme/qute/ItemResource.java
and enter the following:
package org.acme;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.CheckedTemplate;
@Path("item")
public class ItemResource {
@Inject
ItemService service;
@CheckedTemplate
public static class Templates {
public static native TemplateInstance item(Item item);
}
@GET
@Path("{id}")
@Produces(MediaType.TEXT_HTML)
public TemplateInstance get(@PathParam("id") Integer id) {
return Templates.item(service.findItem(id));
}
}
Templates
inner class is created with a method called item()
. That method provides a TemplateInstance
for templates/ItemResource/item.html
and declare its Item item
parameter so Quarkus can validate the template.id
. This parameter is used to render the template when the REST endpoint is called. You can test this endpoint out by running:
curl http://localhost:8080/item/1
You should see an HTML result that shows "Apple" and its price:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Apple</title>
</head>
<body>
<h1>Apple</h1>
<div>Price: 1.9899999999999999911182158029987476766109466552734375</div>
</body>
</html>
Template Parameter Declaration Inside the Template
Alternatively, to declare that a template is expecting an Item
type, you can declare it in the template file itself by adding an additional type and parameter name. This simplifies the Java code while still maintaining type checking.
src/main/resources/templates/ItemResource/item.html
file and replace the existing contents with the following body:
{@org.acme.Item item}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{item.name}</title>
</head>
<body>
<h1>{item.name}</h1>
<div>Price: {item.price}</div>
</body>
</html>
Notice the first line, which is an optional parameter declaration. If declared, Qute attempts to validate all expressions that reference the parameter item
.
ItemResource
class needs to be updated (and simplified) to let the type checking occur by the template itself. Open the src/main/java/org/acme/ItemResource.java
file and replace its existing contents with:
package org.acme;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.Location;
import io.quarkus.qute.Template;
@Path("item")
public class ItemResource {
@Inject
ItemService service;
@Inject
@Location("ItemResource/item")
Template item;
@GET
@Path("{id}")
@Produces(MediaType.TEXT_HTML)
public TemplateInstance get(@PathParam("id") Integer id) {
return item.data("item", service.findItem(id));
}
}
curl http://localhost:8080/item/1
If you made any errors, you'll see them immediately in the rendered output (the Live Coding rebuild will fail).
src/main/resources/templates/ItemResource/item.html
file and change the parameter look up on line 6 from:
{item.name}
{item.nonsense}
http://localhost:8080/item/1
Before Moving On
{item.name}
! You won't need to restart the Quarkus process; simply access the REST endpoint again and Quarkus will fix itself.