Mikael Ståldal
07/07/2023, 10:04 AM<option value="a1">A1</option>
<option value="a3">A3</option>
<option value="a6">A6</option>
(without the containing <select>
element.) This is for JVM server-side, I am generating HTML snippets to be requested with AJAX and inserted into the DOM on the client.
I can do this to get it with a wrapping <select>
element:
val models = listOf(IdName("a1", "A1"), IdName("a3", "A3"), IdName("a6", "A6"))
val html: String = createHTML()
.select {
for (model in models) {
option {
attributes["value"] = model.id
+model.name
}
}
}
but how do I get rid of the wrapping element?Cies
07/07/2023, 1:48 PMfor (model in models) { ... }
is not very kotlinesque, try models.forEach { model -> ... }
instead, I think you'll come to love it!Jerry Preissler
08/06/2023, 12:23 PMclass ContentTemplate(idList: List<DokumentId>): Template<HTML> {
val ids = idList
override fun HTML.apply() {
body {
ul {
ids.forEach { it ->
li {
a(UriRenderer.render(it).toString()) { +it.shortString() }
}
}
}
}
}
}
Peter
08/11/2023, 7:04 AMkotlinx-html
(the JVM version) in combination with htmx
, and liking it a lot so far. 👍
Of course typed HTML is a big advantage, but one aspect I think isn’t highlighted as much but equally as important IMHO is: much better security out of the box.
Because it isn’t a simple string-based template engine, it knows when to escape input (text nodes and attributes) and reduces common risk like user injected XSS.Peter
08/15/2023, 1:06 PMkotlinx-html
with HTMX
and struggling a bit to nicely return (a wellformed) snippet. So far my solutions feel like a hack with some additional string manipulation.
So for example I want to return single parent “DIV” with some children (and not a BODY or HTML tag). What would be the idiomatic way to achieve this in a standard ktor route handler (PipelineContext)?
TIA!!Cies
08/15/2023, 1:48 PMCies
08/15/2023, 1:49 PMCies
08/15/2023, 1:49 PMCies
08/15/2023, 1:52 PM/** Evaluate a TemplateRenderer block into a String. Used in email rendering. */
fun stringFromHtml(renderer: TemplateRenderer): String {
return StringBuilder().apply {
append("<!DOCTYPE html>\n")
appendHTML().renderer()
}.toString()
}
/** Evaluate a TemplateRenderer block into a RePlay RenderHtml Result. <------- so this would be different for KTor! */
fun resultFromHtml(renderer: TemplateRenderer): RenderHtml {
return RenderHtml(stringFromHtml(renderer))
}
typealias TemplateRenderer = TagConsumer<*>.() -> Unit
typealias LayoutBuilder<T> = TagConsumer<*>.(data: T, renderer: TemplateRenderer) -> Unit
fun <T> layout(builder: LayoutBuilder<T>): LayoutBuilder<T> {
return { data, renderer -> builder(data, renderer) }
}
// In a render method we simply do:
fun render(input: Whatever): RenderHtml {
return resultFromHtml {
table {
tr {
+input.toString()
}
}
}
}
Cies
08/15/2023, 1:53 PMadambrangenberg
08/15/2023, 10:04 PMSlackbot
08/21/2023, 9:15 PMPeter
08/25/2023, 9:55 AMHTMX
and kotlinx-html
. Turns out it is not difficult to create inner html snippets using the filter
option. Simple example:
fun createInnerUL(block: UL.() -> Unit): String {
var first = true
return createHTML().filter {
if (first && it.tagName == "ul") {
first = false
SKIP
} else {
PASS
}
}.ul { block() }
}
val a = createInnerUL {
li { +"one" }
li { +"two" }
li {
ul {
li {
+"two-a"
}
}
}
}
println(a)
<li>one</li>
<li>two</li>
<li>
<ul>
<li>two-a</li>
</ul>
</li>
adambrangenberg
09/03/2023, 4:00 PMPeter
09/04/2023, 2:16 PMTobias Wohlfarth
09/10/2023, 6:33 PMArtur L
09/18/2023, 12:07 PMCies
09/18/2023, 12:30 PMsmallufo
10/25/2023, 4:39 PMReuben Firmin
10/26/2023, 7:58 PMReuben Firmin
11/14/2023, 2:17 PMcompanion object {
fun <R> TagConsumer<R>.kpis() {
vs
companion object {
fun FlowContent.kpis() {
I want to call this function:
a) from the DSL
b) when building an html fragment like this:
fun render(): String {
return buildString {
appendHTML().kpis()
}
}
I can make the FlowContent variation at least compile with:
fun render(): String {
return buildString {
appendHTML().div {
kpis()
}
}
}
... but it blows up in weird waysReuben Firmin
11/15/2023, 9:23 PMReuben Firmin
11/16/2023, 11:34 AMclass Column(heading: String, consumer : TagConsumer<*>): DIV(mapOf(), consumer) {
init {
h1 {
+heading
}
}
}
and then within the dsl call it as:
appendHTML().div("p-4") {
...
Column("Column 1", consumer)
Column("Column 2", consumer)
Column("Column 3", consumer)
Column("Column 4", consumer)
Column("Column 5", consumer)
this is pretty nice, because now I can create components in a typesafe way. what I don't like is having to explicitly pass in consumer. is there a better pattern for this?Reuben Firmin
11/29/2023, 12:01 PMReuben Firmin
11/29/2023, 10:06 PMcreateHTMLDocument
consumer (i.e. the original render) but fails when it's part of the appendHTML
consumer. source in thread. why is this happening?ilyagulya
01/08/2024, 8:08 AMTagConsumer<HTMLElement>
as a receiver, not a TagConsumer<Element>
?
I’m asking since I’m currently adding support for wasmJs
target and during this process I’ve found out that there’s one particular builder which is failing - svg
.
SVGElement
does not inherirt HTMLElement
which leads to ClassCastException
.
I’m assuming that this works fine in Kotlin/JS because of its dynamic nature.
Now I have 2 options:
1. Change the receiver of builder functions from TagConsumer<HTMLElement>
to TagConsumer<Element>
(this one is quite big change, most likely breaking change). Do anyone see there any consequences? Is there any reason to depend specifically on HTMLElement
instead of Element
?
2. Skip SVG in wasmJs
target for now and leave it for the future users to figure out 🙂e5l
01/23/2024, 2:49 PMFunyinoluwa Kashimawo
02/13/2024, 10:57 AMAlex Fairley
02/14/2024, 3:07 PMval htmlString = buildString {
appendHTML().html {
// build html here
}
}
but the html file is getting to long for StringBuilder to handle and I am running out of java heap space. Aside from allocating more heap space to my process would another (stream based) approach work?Alex Fairley
02/15/2024, 3:56 PM<div class="a">
<div class="b">
<div class="c"></div>
<div class="c"></div>
</div>
</div>
How does appendHTML() work? Does it wait for a tag to be closed before appending or does it append everytime it encounters a new tag (the latter behaviour would be desired for my use case)