How do I generate a non-well-formed HTML snippet, ...
# kotlinx-html
m
How do I generate a non-well-formed HTML snippet, containing multiple elements without a wrapping element? E.g.
Copy code
<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:
Copy code
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?
c
you can make a custom element that does not respect the HTML5 spec. You can also hackishly remove the
select
element before sending the snippet out.
m
But I don't want a custom element, I want no element.
c
The custom element can be for
option
then you can make it in a way that --unlike the non-custom
option
element-- it can occur everywhere in the HTML (and not only in a
select
)
So you dont want a custom "select" but oyu do want a custom "optoin"
m
OK, but I don't have a problem to create one non-custom
option
element without a `select`:
Copy code
val html: String = createHTML().option {
        value = "value"
        +"name"
    }
c
If one works, all work. I think you solved it!
m
No, since I get a
String
and cannot add more elements.
Or well, I can do this:
Copy code
models.joinToString(separator = "") { model ->
                    createHTML().option {
                        value = model.id
                        +model.name
                    }
                }
But it is not very nice.
I think I found a proper solution:
Copy code
@HtmlTagMarker
inline fun <T, C : TagConsumer<T>> C.fragment(crossinline block: TagConsumer<T>.() -> Unit): T {
    try {
        this.block()
    } catch (err: Throwable) {
        this.onTagError(HTMLTag("", this, emptyMap(), null, inlineTag = false, emptyTag = false), err)
    }
    return this.finalize()
}
So I can do this:
Copy code
createHTML().fragment {
        models.forEach { model ->
            option {
                value = choice.id
                +choice.name
            }
        }
    }
Or no, that does not work properly, it ends up calling
TagConsumer.finalize()
multiple times. Back to the drawing board.
Can do this:
Copy code
@HtmlTagMarker
inline fun <T, C : TagConsumer<T>> C.fragment(crossinline block: DIV.() -> Unit): T {
    DIV(emptyMap(), this).block()
    return this.finalize()
}
But not for all elements, not for e.g.
<tr>
and
<option>
.