anybody have a technique to allow building a fragm...
# kotlinx-html
r
anybody have a technique to allow building a fragment with the dsl, but get back an HTMLTag (i.e. a consumer that builds a tag hierarchy rather than a string)? i'm looking at implementing support for hx-swap-oob, a feature of htmx; it allows you to add supplementary tags to the response which get swapped with other parts of the dom from the main element that you're targeting. (e.g. maybe you want to update a bunch of existing table cells selectively.) to do this i need to add an attribute (and an id) to the container for each of the tags that i'm adding; i want to provide this as a mostly automatic feature for callers of this function. since i can't add an id and attribute to a string, i need to get back an HTMLTag (or, actually, the very weirdly named CommonAttributeGroupFacade, because i need to set id) at the point at which i'm composing everything together. i.e. i want something like this in my library:
Copy code
data class AuxResponse(val target: HTMXTarget, val tag: TagConsumer<*>.() -> CommonAttributeGroupFacade)

    fun render(target: HTMXTarget,
               vararg composed: AuxResponse,
               block: FlowContent.() -> Unit): Response {
which ideally users of the library could call with:
Copy code
render(someTarget, 
    AuxResponse(someOtherTarget) { 
       some_adapter {
         p {
            +"Hello world supplemented"
         }
       }
    )) {
        p {
           +"Hello world main"
        }
I gave up and did it the easy way. AuxResponses always swap out the element being targeted with a div. known limitation for now.
Copy code
fun Context.render(target: HTMXTarget,
               vararg composed: AuxResponse,
               block: FlowContent.() -> Unit): Response {

        return Response(buildString {
            with(appendHTML()) {
                div(classes = "wrapper") {
                    // take off the wrapper div.
                    noWrapper()
                    block()
                }

                composed.forEach {
                    div {
                        id = it.target.id
                        attributes["hx-swap-oob"] = "true"
                        it.subrender(this@div, this@render)
                    }
                }
            }
        }, target)
    }
+
Copy code
data class AuxResponse(
    val target: HTMXTarget,
    val subrender: FlowContent.(Context) -> Unit)
demo:
Copy code
class ExperimentPage @Inject constructor(): Api {

    val experimentTarget = HTMXTarget("foo")
    val experiment2Target = HTMXTarget("foo2")
    val experiment3Target = HTMXTarget("foo3")

    @GET("/experiment")
    fun getPage() = page {

        div {
            target(experiment2Target)
        }

        div {
            target(experiment3Target)
        }

        +"Hello world"

        div {
            // this makes an api call from the browser
            onLoad(::helloName) {
                it.param("name", "james")
                  .param("num_arrows", "5")
            }
            target(experimentTarget)
        }
    }

    @GET("/hello")
    fun helloName(context: Context) = context.render(experimentTarget,

        AuxResponse(experiment2Target) {
            helloOut()
        },
        AuxResponse(experiment3Target) { ctx ->
            val numArrows = ctx.queryParamStrict("num_arrows")!!.toInt()
            arrows(numArrows)
        }
    ) {
        val name = context.queryParamStrict("name")
        p {
            +"Hello $name"
        }
        deleteIcon("")
    }

    private fun FlowContent.helloOut() {
        p {
            +"Hello first out of band target!"
        }
    }

    private fun FlowContent.arrows(n: Int) {
        repeat(n) {
            rightArrowIcon("w-5 h-5")
        }
    }
}
2024-04-13_15-13.png
More useful example:
Copy code
@GET("/billing/payment_method", Role.USER)
    fun getPaymentMethod(ctx: Context): Response  {
        val payment = paymentService.getPaymentMethod(userDAO.get(ctx.authUser().identity!!.userId))

        return if (payment == null) {
            ctx.render(paymentMethodTarget) {
                paymentMethodForm(ctx.authUser().withNoEntity())
            }
        } else {
            ctx.render(paymentMethodTarget,
                AuxResponse(subscriptionDetailsTarget) {
                    subscriptionDetails()
                }
            ) {
                paymentMethodSummary(payment.first, payment.second)
            }
        }
    }
a
Hi, Actually there's an existing library that helps in htmx with spring boot, You can do button{ hx{} } Also I have a solution to pass the block as an argument, But most of the time calling hx{} must be the first thing in the lambda block
It's called htmx spring boot