Kotlin / kotlinx.html

Kotlin DSL for HTML
Apache License 2.0
1.6k stars 130 forks source link

Please make the streaming consumer easier to work with #249

Open reubenfirmin opened 8 months ago

reubenfirmin commented 8 months ago

When creating custom components, it's very easy to get attributes and tag properties in the wrong order, which leads you to run into:

java.lang.IllegalStateException: You can't change tag attribute because it was already passed to the downstream
    at kotlinx.html.consumers.DelayedConsumer.onTagAttributeChange(delayed-consumer.kt:16)

This can be avoided by reordering code, but it's ultimately an implementation detail of the library. Please provide either an update to the streaming consumer so that it isn't sensitive to attribute vs content order, or provide an alternate consumer allowing creation of html fragments which is non-streaming.

reubenfirmin commented 8 months ago

As another example (and I think indicative of this being a design problem), the DSL has:

@HtmlTagMarker
inline fun <T, C : TagConsumer<T>> C.div(classes : String? = null, crossinline block : DIV.() -> Unit = {}) : T = DIV(attributesMapOf("class", classes), this).visitAndFinalize(this, block)

and

@HtmlTagMarker
inline fun FlowContent.div(classes : String? = null, crossinline block : DIV.() -> Unit = {}) : Unit = DIV(attributesMapOf("class", classes), consumer).visit(block)

The latter can be used almost anywhere. The only place that I know the former can be used is strung from the appendHTML() function. The problem with this is that replacing an element (e.g. via htmx) can sometimes result in awkward workarounds to avoid the hierarchy changing, because most custom components are written only with FlowContent in mind.

reubenfirmin commented 5 months ago

Another example:

This blows up with the above exception: 2024-03-14_13-26

This works fine: 2024-03-14_13-29

So it looks like a simple change in receiver triggers the flush of the tag.