Skip to content

Micro templating and DSL customizing

Sergey Mashkov edited this page Jul 6, 2015 · 5 revisions

Customizing DSL

It is very likely that you page may have repeated parts, for example "components" or containers that is typical for your application and you don't want to copy-past it. The most beginning solution is to extract such things to functions.

For example, consider example with Twitter Bootstrap with dropdowns:

li {
    classes = setOf("dropdown")

    a("#", null) {
        classes = setOf("dropdown-toggle")
        attributes["data-toggle"] = "dropdown"
        role = "button"
        attributes["aria-expanded"] = "false"

        ul {
            classes = setOf("dropdown-menu")
            role = "menu"

            li { a("#") { +"Action" } }
            li { a("#") { +"Another action" } }
            li { a("#") { +"Something else here" } }
            li { classes = setOf("divider")}
            li { classes = setOf("dropdown-header"); +"Nav header" }
            li { a("#") { +"Separated link" } }
            li { a("#") { +"One more separated link" } }
        }

        span {
            classes = setOf("caret")
        }
    }
}

Looks not so simple as should be, isn't it? To get it look better we can introduce some extension functions to extend DSL

Let's introduce top level function dropdown like this:

fun UL.dropdown(block : LI.() -> Unit) {
    li("dropdown") {
        block()
    }
}

As you can see the function dropdown can be called on <UL> and consumes lambda with this of type LI. So content inside this lambda will be executed inside <LI>. Inside we construct li with class dropdown and call lambda inside it.

We introduce more extension functions to be able to shortcut even more code

fun LI.dropdownToggle(block : A.() -> Unit) {
    a("#", null, "dropdown-toggle") {
        attributes["data-toggle"] = "dropdown"
        role = "button"
        attributes["aria-expanded"] = "false"

        block()

        span {
            classes = setOf("caret")
        }
    }
}

fun LI.dropdownMenu(block : UL.() -> Unit) : Unit = ul("dropdown-menu") {
    role = "menu"

    block()
}

fun UL.dropdownHeader(text : String) : Unit = li { classes = setOf("dropdown-header"); +text }
fun UL.divider() : Unit = li { classes = setOf("divider")}

After that we can create dropdowns easier

createHTML().ul {
    dropdown {
        dropdownToggle { +"Dropdown" }
        dropdownMenu {
            li { a("#") { +"Action" } }
            li { a("#") { +"Another action" } }
            li { a("#") { +"Something else here" } }
            divider()
            dropdownHeader("Nav header")
            li { a("#") { +"Separated link" } }
            li { a("#") { +"One more separated link" } }
        }
    }

It looks much more clear and it is easier to understand and modify