Templating

Installation (Gradle)

dependencies {
    implementation(platform("org.http4k:http4k-bom:5.32.2.0"))

    // Dust: 
    implementation("org.http4k:http4k-template-dust")

    // Freemarker: 
    implementation("org.http4k:http4k-template-freemarker")

    // Handlebars: 
    implementation("org.http4k:http4k-template-handlebars")

    // Pug4j: 
    implementation("org.http4k:http4k-template-pug4j")

    // JTE: 
    implementation("org.http4k:http4k-template-jte")

    // Pebble: 
    implementation("org.http4k:http4k-template-pebble")

    // Rocker: 
    implementation("org.http4k:http4k-template-rocker")

    // Thymeleaf: 
    implementation("org.http4k:http4k-template-thymeleaf")
}

About

The http4k templating API provides a standard mechanism for rendering using common templating libraries. Simply implement the ViewModel interface on a model class and pass it to the renderer to get a string. All of the implementations support view rendering using the following strategies:

  • Cached on the classpath
  • Cached from the filesystem
  • Hot-Reloading from the filesystem

The examples below are for Handlebars, but the others have the same APIs:

Code

package guide.reference.templating

import org.http4k.core.Body
import org.http4k.core.ContentType.Companion.TEXT_HTML
import org.http4k.core.HttpHandler
import org.http4k.core.Method.GET
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status.Companion.OK
import org.http4k.core.with
import org.http4k.template.HandlebarsTemplates
import org.http4k.template.ViewModel
import org.http4k.template.viewModel
import java.io.File

data class Person(val name: String, val age: Int) : ViewModel

fun main() {

    // first, create a Renderer - this can be a Caching instance or a HotReload for development
    val renderer = HandlebarsTemplates().HotReload("src/test/resources")

    // first example uses a renderer to create a string
    val app: HttpHandler = {
        val viewModel = Person("Bob", 45)
        val renderedView = renderer(viewModel)
        Response(OK).body(renderedView)
    }
    println(app(Request(GET, "/someUrl")))

    // the lens example uses the Body.viewModel to also set the content type, and avoid using Strings
    val viewLens = Body.viewModel(renderer, TEXT_HTML).toLens()

    val appUsingLens: HttpHandler = {
        Response(OK).with(viewLens of Person("Bob", 45))
    }

    println(appUsingLens(Request(GET, "/someUrl")))

    // overwrite the content - this will prove the hot reload works!
    File("src/test/resources/guide.reference/templating/Person.hbs").writer()
        .use { it.write("{{name}} is not {{age}} years old") }

    println(appUsingLens(Request(GET, "/someUrl")))
}

Notes for Rocker

Rocker differs slightly from the dynamic templating engines in that it generates Java classes at compile time. In order to fit this into the http4k model, we have created a special superclass RockerViewModel (which combines the Rocker and the http4k ViewModel interfaces into a common supertype). This should be used as the extendsModelClass property in the generation process by configuration. Note that as the generated classes are Java and NOT Kotlin, Java syntax should be used inside the view files (which need to be named Xyz.rocker.html).

Notes for Pebble

The way the underlying model is exposed for Pebble templates differs from the rest of the templating engines. The properties of the ViewModel object are bound to the view context under a model key, rather than directly into the template context itself.

This means that the model. prefix is required to access the properties of the underlying object in a Pebble template.

In other words, rendering a firstName property, for example, is done using:

{{ model.firstName }}

instead of

{{ firstName }}