Given the Kotlinx.html structure:
menu {
menuItem("Home")
menuItem("Blog")
}
sidebar {
sidebarItem("Home")
sidebarItem("Blog")
}
What is the most Kotlinx.html idiomatic way of flattening this structure down to something like this:
navigation {
navigationItem("Home")
navigationItem("Blog")
}
The menu
/menuItem
and sidebar
/sidebarItem
components will always mirror each other so I would like to merge them together and generate them, but I’m having trouble figuring out how I can do that while keeping the appropriate structure. I end up generating something like this:
menu {
sidebar {
menuItem("Home")
sidebarItem("Home")
menuItem("Blog")
sidebarItem("Blog")
}
}
I’d appreciate any help, thanks.
Create an extension function on a parent element, which manually adds children to menu
and sidebar
.
1 Like
I appreciate you taking the time to respond to my inquiry. How does adding an extension function to a parent element allow me to generate Kotlinx.html with separate scopes though? menu
and sidebar
are already extension functions of FlowContent
. Like so:
fun FlowContent.menu(block: DIV.() -> Unit) {
}
fun FlowContent.sidebar(block: DIV.() -> Unit) {
}
fun FlowContent.navigation(block: DIV.() -> Unit) {
}
The issue comes from the fact that in order for:
navigation {
navigationItem("Home")
navigationItem("Blog")
}
To generate:
menu {
menuItem("Home")
menuItem("Blog")
}
sidebar {
sidebarItem("Home")
sidebarItem("Blog")
}
I need to somehow add logic to the Kotlinx.html DSL which I’m not even sure is possible.
What would the psuedo code look like? My thinking goes:
Create list of all the "navigationItem" entries passed to "navigation"
Create "menu" Kotlinx.html scope
Iterate list emitting "menuItem"
Close "menu" Kotlinx.html scope
Create "sidebar" Kotlinx.html scope
Iterate list emitting "sidebarItem"
Close "sidebar" Kotlinx.html scope
What’s preventing me from implementing this logic is that the value of block
isn’t a list of everything that was passed to it. It’s an empty lambda with just an aritiy
value. So I don’t see how to Create list of all the "navigationItem" entries passed to "navigation"
.
Sorry, did not have time to respond in detail. But since I’ve started, I will explain. Obviously you can’t trigger both event listeners simultaneously. So in order to do that you need to remember those items and then add them item by item. Here is the example:
class MenuItems{
private val items = ArrayList<String>() // You can store DIV.()->Unit functions as well
fun item(name: String){ items.add(name)}
internal fun applyToSideBar(sideBar: SideBar) = sideBar.run{
items.forEach{ sidebarItem(it)}
}
internal fun applyToMenu(menu: Menu) = menu.run{
items.forEach{ sidebarItem(it)}
}
}
// the extension
fun FlowContent.items(block: MenuItems.()->Unit){
val items = MenuItems().apply(block)
menu{
items.applyToMenu(this)
}
sidebar{
items.applyToSidebar(this)
}
}
The other way is to work not with FlowContent, but with DOM strucutre directly. This way you can take a tag, then get its children and append elements to both branches.
1 Like
Thank you, @darksnake. Using your example I was able to solve my issue plus I was finally forced to learn what () -> Unit
actually means, which is an added bonus.