Comments (11)
But OK, I tried to make it more clear.
from kotlinx.html.
I bumped into this issue when trying https://htmx.org with a ktor+kotlinx.html backend. Note that the issue does not only concern "multiple elements" as I might want to replace a single <li>
in a list. kotlinx.html does not allow me to build a <li>
without a containing <ol>
or <ul>
.
Luckily there is a workaround in https://htmx.org : using a hx-select
attribute I can ignore the containing <ol>
in the response and only "select" the <li>
element. This also works with multiple elements.
from kotlinx.html.
Possible solution:
@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()
}
from kotlinx.html.
Or no, that does not work properly, it ends up calling TagConsumer.finalize()
multiple times. Back to the drawing board.
from kotlinx.html.
"non-well-formed" sounds like the use of elements that are either missing an opening tag or a closing tag, but not both. If you only read the title of this issue, it sounds like you are trying to use a bad (non-well-formed) approach, but the thing you ask for is perfectly fine. "multiple elements with no containing element" is clearer IMO.
I also need a solution for this. Every time you want to reuse code (by using a function call), you are forced to use an unwanted container element, such as a div
. But I'm now in the situation where I need code reuse in the HTML head
, so I cannot use div
anymore....
from kotlinx.html.
I am using the XML definition of well-formedness: https://www.w3.org/TR/2008/REC-xml-20081126/#sec-well-formed
from kotlinx.html.
You could do something like this:
val result: String = buildString {
appendLine(createHTML().option {})
appendLine(createHTML().option {})
appendLine(createHTML().option {})
}
println(result)
But these intermediate strings do limit performance and it's an ugly solution, especially when the number of root level elements grows.
Update: or better:
val result: String = buildString {
appendHTML().option { }
appendHTML().option { }
appendHTML().option { }
}
println(result)
I believe that sooner or later, everybody who uses kotlinx.html on an intermediate level, will sooner or later bump into this problem. The best solution would be if there was an attribute to signal that the root level element should be skipped, like:
val html: String = createHTML()
.select {
skipRoot = true // changed
for (model in models) {
option {
value = model.id
+model.name
}
}
}
or something like this:
val html: String = createHTML()
.container { // changed
for (model in models) {
option {
value = model.id
+model.name
}
}
}
from kotlinx.html.
This solutions works, but it requires a pull request.
Change kotlinx.html.Tag.tagName
from type String
to String?
. Then fix all usage of that property, which usually means code like:
if (tag.tagName == null) {
return
}
(Only about 5 files have to be modified.)
Then you can use this:
fun main() {
val r = createHTML().emptyRoot {
div { }
div { }
}
println(r) // prints: <div></div> <div></div>
}
inline fun <T, C : TagConsumer<T>> C.emptyRoot(classes : String? = null, crossinline block : EMPTY_ROOT.() -> Unit = {}) : T = EMPTY_ROOT(
attributesMapOf(),
this
).visitAndFinalize(this, block)
open class EMPTY_ROOT(initialAttributes : Map<String, String>, override val consumer : TagConsumer<*>) : HTMLTag(null, consumer, initialAttributes, null, false, false), HtmlBlockTag
from kotlinx.html.
Are there any updates? I'd like to see this implemented as well. I'm down to help out if you want.
from kotlinx.html.
It's not perfect but you can copy the li
method onto FlowContent, e.g.
inline fun FlowContent.li(classes: String? = null, crossinline block: LI.() -> Unit = {}) =
LI(attributesMapOf("class", classes), consumer).visit(block)
My solution to the empty root problem:
inline fun partial(crossinline block: FlowContent.() -> Unit) = createHTML {
object : FlowContent {
override val attributes = DelegatingMap(emptyMap(), this) { this@createHTML }
override val attributesEntries: Collection<Map.Entry<String, String>>
get() = this.attributes.immutableEntries
override val consumer: TagConsumer<*>
get() = this@createHTML
override val emptyTag: Boolean
get() = false
override val inlineTag: Boolean
get() = false
override val namespace: String?
get() = null
override val tagName: String
get() = ""
}.block()
}
from kotlinx.html.
Came across this issue while researching. Posting my solution here if anyone needs it:
import kotlinx.html.*
class FRAGMENT(override val consumer: TagConsumer<*>) : HTMLTag(
tagName = "fragment",
consumer = consumer,
initialAttributes = emptyMap(),
emptyTag = false,
inlineTag = false,
namespace = null,
), FlowContent
@HtmlTagMarker
inline fun <T, C : TagConsumer<T>> C.fragment(
crossinline block: FRAGMENT.() -> Unit = {}
): T = FragmentAwareTagConsumer(this).let { FRAGMENT(it).visitAndFinalize(it, block) }
class FragmentAwareTagConsumer<T>(private val delegate: TagConsumer<T>) : TagConsumer<T> by delegate {
override fun onTagStart(tag: Tag) {
if (tag !is FRAGMENT) delegate.onTagStart(tag)
}
override fun onTagEnd(tag: Tag) {
if (tag !is FRAGMENT) delegate.onTagEnd(tag)
}
}
Usage:
buildString {
appendHTML().fragment {
div {
+"Hello, world from div!"
}
}
appendHTML().fragment {
p {
+"Hello, world from paragraph!"
}
}
}
from kotlinx.html.
Related Issues (20)
- Cannot disable escaping of `&` in an attribute value HOT 2
- iOSARM64 artifact for 0.9.1 missing from Maven Central HOT 5
- A confusion with types for `Event`
- Options for generating the HTML Code
- Escaping attribute value characters break Unicode codes
- HTML5 <u> tag is missing
- Can't Download kotlinx-html-jvm from https://maven.pkg.jetbrains.space HOT 3
- Please adopt html to kotlinx.html Intellij plugin HOT 1
- Improve the documentation on custom tags
- Cannot put a div inside a th
- SAXParseException when trying to add an unclosed, raw `<link>` tag into a `head { ... }` block HOT 4
- Please make the streaming consumer easier to work with HOT 2
- htmlx
- Missing release(s), e.g. 0.11 HOT 2
- Adjust readme file to replace kotlin-js with wasm
- Add all aria attributes
- @Preview plugin for Intellij [feature]
- Rename TagConsumer.finalize()
- `attributes["hx-on::after-request"]` does not preserve double colon HOT 3
- Support path tag
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from kotlinx.html.