How do I provide a page template in kotlinx-html?

Viewed 55

I would like to generate a bunch of HTML files with kotlinx-html and I want to start each file with the same template. I would like to have a function for the base structure and provide a lamda to this function for the specific content like so (non working code):

// provide block as a div for the sub content, does not work!
private fun createHtmlPage(block : () -> DIV.()): String {
    val html = createHTMLDocument().html {
        head {
            meta { charset = "utf-8" }
            meta { name="viewport"; content="width=device-width, initial-scale=1" }
            title { +"Tables" }
            link(href = "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css", "style")
        }
        body {
            block {}
            script("", "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js") {}
        }
    }
    return html.serialize(true)
}

and use this function like this (again non working code):

private fun createIndexPage(tables: Tables) {
    val indexFile = File(path, "index.html")

    // call my template function with a lamda - does not work
    val html = createHtmlPage {
        h1 { +"Tables" }
        tables.tableNames.forEach { tableName ->
            a("${tableName}.html") {
                +tableName
            }
            br
        }
    }
    indexFile.writeText(html)
}

Can anyone point me in the direction how to do this?

Additional question

I have found out that project Ktor HTML DSL exists and they have template support on top of kotlinx-html. Am I supposed to use this library instead of kotlinx-html directly? Is it possible to use it without Ktor?

1 Answers

You can use the ktor-server-html-builder library without a server. Here is an example:

import io.ktor.server.html.*
import kotlinx.html.*
import kotlinx.html.stream.appendHTML

fun main() {
    val template = LayoutTemplate().apply {
        header {
            +"Ktor"
        }
        content {
            articleTitle {
                +"Hello from Ktor!"
            }
            articleText {
                +"Kotlin Framework for creating connected systems."
            }
        }
    }

    val builder = StringBuilder()
    builder.appendHTML().html(block = {
        with(template) { apply() }
    })

    println(builder.toString())
}

class LayoutTemplate: Template<HTML> {
    val header = Placeholder<FlowContent>()
    val content = TemplatePlaceholder<ContentTemplate>()
    override fun HTML.apply() {
        body {
            h1 {
                insert(header)
            }
            insert(ContentTemplate(), content)
        }
    }
}

class ContentTemplate: Template<FlowContent> {
    val articleTitle = Placeholder<FlowContent>()
    val articleText = Placeholder<FlowContent>()
    override fun FlowContent.apply() {
        article {
            h2 {
                insert(articleTitle)
            }
            p {
                insert(articleText)
            }
        }
    }
}

I recommend implementing your own layer on top of kotlinx-html for templating because that's not hard to do and it will solve your particular problem better.

Related