https://kotlinlang.org logo
#multiplatform
Title
# multiplatform
g

Gunslingor

03/22/2020, 3:11 AM
Does kotlinX html transpile to html or javascript that later generates html, seems to be the later to me... does anyone know if thats better or worse, I would imagine worse. Not sure what kotlinx html for jvm is compiling too... a string buried in a jar maybe? I mean which is better, generating html templates with kolinx jvm, js or common (would common work)? I suspect its best to generate the templates at the bad side and then send pure html that gets modified... but If my JS is in my frontend and my templates are in my backend how could I ever organize by module?
s

spierce7

03/22/2020, 5:17 AM
It’s the latter. It’s generating the HTML string on the fly. The benefit is that you can have dynamically generating HTML with if statements etc. As for how it’s compiled - you are overthinking it. It’s not doing something special at compilation time and compiling down into an optimized String. You are just writing Kotlin code that knows how to output a String, and so that’s what it compiles down to. Java Byte code that outputs a String from a StringBuilder.
If my JS is in my frontend and my templates are in my backend how could I ever organize by module?
I’m not sure what you mean. If you want you can generate the html on the server, or you could generate it on the client side, whichever has the best pros / cons for what you are doing.
👍 1
I played around with kotlinx.html a lot. I found it undersupported and lacking in several areas (i.e. it’s API, and other things like CSS). Because of that I created my own html library. https://github.com/ScottPierce/kotlin-html
It has some performance testing that I did for it on some earlier versions, and it’s better than kotlinx.html. It heavily uses inline classes, and the entire thing basically compiles down into a giant wrapped
StringBuilder
.
g

Gunslingor

03/23/2020, 12:41 AM
Interesting, your the guy that wrote that? Caused me some confusion as to how it related to the kotlinx version. Yeah... not sure if I'm sold on this programming html and css stuff yet. to be honest its so tough to setup and keep well maintained not sure if it's worth it. I think I understand now that createHTML return strings so I could just output those to template files at runtime rather than what I was doing:
Copy code
document.getElementById("projection")?.innerHTML = createHTML {blah blah}
The above was going beyond just a long html string, it was generating each element with JS not HTML. Still not sure how to link these generated templates back in to the JS stuff but I presume that will present itself along the way? Really wondering if my goal of programming everything in one multiplatform kotlin project is futile, lol.
ATM I'm trying to figure out how to link 2 of these createHTML strings.... plus works as expected but what if I wanna put two together in divs like this:
Copy code
class DoublePaneView(val paneA: String, val paneB: String) {
    val view = createHTML().div {
        div {
            paneA
        }
        div {
            paneB
        }
    }
}
I considered using yours
s

spierce7

03/23/2020, 1:29 AM
@Gunslingor Kotlin-html that I wrote is a complete replacement for kotlinx.html. They are not integrated at all. Here is how you’d do that with kotlin-html. I’m going to try and make a few other suggestions, that you represent your DoublePaneView with a function instead of a class.
Copy code
fun BodyContext.doublePaneView() {
    div {
        div {
            paneA()
        }
        div {
            paneB()
        }
    }
}

fun BodyContext.paneA() {
    div(
        style = {
            displayFlex()
            flexDirection(FlexDirection.COLUMN)
        }
    ) {
        +"Panel A"
    }
}

fun BodyContext.paneB() {
    div {
        +"Panel B"
    }
}
Then you can use these functions like this:
Copy code
fun main() {
    StringHtmlOutput().html {
        body {
            doublePaneView()
        }
    }
}
I threw an example of some css for fun.
g

Gunslingor

03/23/2020, 1:33 AM
Okay, thanks I'll check that out... you really think your plugin is better maintained than kotlinx though? I do think it needs to stay a class though... lots of functions and values are going to be associated to it later.
s

spierce7

03/23/2020, 1:35 AM
I think you’ll find that functions are a more flexible means of accomplishing re-usable, composable code.
you really think your plugin is better maintained than kotlinx though?
I just found myself super frustrated with kotlinx. And the library had longstanding issues from 3+ years ago, and they weren’t going to get solved.
I think the kotlinx library is more complete. I think it probably has ALL the html elements. I think I’m missing a few here and there. As people file issues I’m adding them pretty quickly. I think my library is a much better basis to move forward with.
You said earlier that you weren’t sold on using kotlin to write html - that’s fair. The primary benefit is that you can re-use code on multiple screens. That’s really it. There are other server platforms that offer using jsx to output html, and that ends up being somewhat similar. It’s really a matter of preference
I used my library to write https://www.arseldrivingschool.com/ Not that this is directly related, but I was about to get very good performance numbers out of the HTML that it outputs, and also the server that it runs on, however that’s also possible with kotlinx.html - https://lighthouse-dot-webdotdevsite.appspot.com//lh/html?url=https%3A%2F%2Fwww.arseldrivingschool.com%2F
g

Gunslingor

03/23/2020, 1:42 AM
Still learning all this.. not sure it's worth it... I'm missing js/html/php lol. Yeah... I mean the benefit of getting rid of all these old ass obscure "things" like js/html/css for something programmatic and that's only one "thing"... thats the real benefit, sky is the limit at that point... but only if its easy to get started fast.
Nice page, fast
I want to make sure that this project generates a tradition unwebpacked traditional MVC version of this project, essentially what I would've build 5 years ago... I guess that way if all this goes bye bye I'm still covered... this is going to be huge enterprise software, not sure its smart to use all this but if I'm custom, careful and it could have massive benefits later... learning gradle fairly well at this point, but still spend days trying to solve single function issues, lol.
s

spierce7

03/23/2020, 2:10 AM
how familiar are you with Kotlin?
g

Gunslingor

03/23/2020, 2:17 AM
I took a 40 hour course on it, used it at work for maybe 3 months for java... all in all I understand it all, except for some of the more advanced stuff like defining my own receive functions and stuff like "fun <T, V>something (Object.()->whatever, lol): Thing<T>(Object<V>.())" lol that stuff I kinda understand but do not want to understand... like the definition of an adverb 😃
I hate java
C++ is good but only for low level... web techs I got good at but trying the new stuff
Great wiith python...
s

spierce7

03/23/2020, 2:21 AM
lol - I think Kotlin is better than all those other languages. It’s the best application level language there is.
g

Gunslingor

03/23/2020, 3:36 AM
I think so too for the most post... only thing python has over kotlin is that it can be shorter.
So the generate front end js is this, using kotlin html js document.create.div.
function DoublePaneView$view$lambda($receiver) {
visitTag(new DIV_init(attributesMapOf('class', 'd-inline-block col-md-6 col-sm-12'), $receiver.consumer), visit$lambda(DoublePaneView$view$lambda$lambda));
visitTag(new DIV_init(attributesMapOf('class', 'd-inline-block col-md-6 col-sm-12'), $receiver.consumer), visit$lambda(DoublePaneView$view$lambda$lambda_0));
return Unit;
}
When I do the same html in the backend, I find the following string in the file \build\libs\FL-backend-1.0-SNAPSHOT\com\company
t ' 	real-body " !d-inline-block col-md-6 col-sm-12 • 	left_pane —
So I could (a) output the from js html templates I think that the backend consumes, (b) I could let JS do all the html building like the example above, or (c) I could generate the templates in the backend and get them packaged into this file I don't understand.... I'd like to understand it but I don't know what it is really. Not sure how to make a work but suspect gradle is supposed to control it. Can you confirm b is too slow in the long term, it seems fast now but I don't know? And if you have any hints or sources so I can better understand what that jar is, how it gets made by gradle and how my html ends up inside it... would be appreciated.
s

spierce7

03/23/2020, 7:41 PM
I really think you are over-thinking it.
You can see my benchmark at the bottom of the readme for kotlin-html
it’s very fast
When I do the same html in the backend, I find the following string in the file \build\libs\FL-backend-1.0-SNAPSHOT\com\company
I have no idea what you are looking at
That also looks like it’s kotlinx.html, which I can’t really help you with. I stopped using it after a day of frustration with it.
g

Gunslingor

03/23/2020, 7:46 PM
I'm just realizing the way kotlin html works, there are really never any templates at all... in js ita is that generates html, in jvm its byte code strings.
But your right, overthinking... I have 1 month to setup a massive project if things work out
s

spierce7

03/23/2020, 8:34 PM
no - in jvm it’s java byte code that generates the html
there is nothing special going on
I think you misunderstood what you were looking at
g

Gunslingor

03/23/2020, 11:33 PM
Yeah, gradle does a ton of stuff out of the box and does it differently than I'm used to, so I'm trying to understand all this.
s

spierce7

03/24/2020, 12:54 AM
I mean - you don’t need to, and you won’t understand java byte code lol
g

Gunslingor

03/24/2020, 12:56 AM
true.... I mean I understand it's essential 256 CISC assembly codes running on a virtual processor...
But no I don't understand how it creates HTML out of these class files on demand, lol
I'm thinking in the end I just use this tool in js or jvm as needed and stop over thinking it like you say, lol, just trying to become an SME but much is involved.
s

spierce7

03/24/2020, 1:02 AM
if you look at my library it’s easier to understand
the output is I mean
I’ll give you an example
Using your above example again:
Copy code
fun BodyContext.doublePaneView() {
    div {
        div {
            paneA()
        }
        div {
            paneB()
        }
    }
}
fun BodyContext.paneA() {
    div(
        style = {
            displayFlex()
            flexDirection(FlexDirection.COLUMN)
        }
    ) {
        +"Panel A"
    }
}
fun BodyContext.paneB() {
    div {
        +"Panel B"
    }
}
g

Gunslingor

03/24/2020, 1:06 AM
Context sounds like a good idea.
s

spierce7

03/24/2020, 1:06 AM
If I compile it, and then decompile it. This is the equivalent java code for what the kotlin compiler wrote. Java has less of a magical appearance than Kotlin:
Copy code
public final class SimpleBenchmarkMainKt {
   public static final void doublePaneView_caaDT_8/* $FF was: doublePaneView-caaDT-8*/(@NotNull HtmlWriter $this$doublePaneView) {
      Intrinsics.checkParameterIsNotNull($this$doublePaneView, "$this$doublePaneView");
      String id$iv = (String)null;
      String classes$iv = (String)null;
      Function1 style$iv = (Function1)null;
      int var5 = false;
      ElementUtilKt.writeNormalElementStart($this$doublePaneView, "div", id$iv, classes$iv, style$iv);
      int var7 = false;
      String id$iv = (String)null;
      String classes$iv = (String)null;
      Function1 style$iv = (Function1)null;
      int var12 = false;
      ElementUtilKt.writeNormalElementStart($this$doublePaneView, "div", id$iv, classes$iv, style$iv);
      int var14 = false;
      paneA-caaDT-8($this$doublePaneView);
      ElementUtilKt.writeNormalElementEnd($this$doublePaneView, "div");
      id$iv = (String)null;
      classes$iv = (String)null;
      style$iv = (Function1)null;
      var12 = false;
      ElementUtilKt.writeNormalElementStart($this$doublePaneView, "div", id$iv, classes$iv, style$iv);
      var14 = false;
      paneB-caaDT-8($this$doublePaneView);
      ElementUtilKt.writeNormalElementEnd($this$doublePaneView, "div");
      ElementUtilKt.writeNormalElementEnd($this$doublePaneView, "div");
   }

   public static final void paneA_caaDT_8/* $FF was: paneA-caaDT-8*/(@NotNull HtmlWriter $this$paneA) {
      Intrinsics.checkParameterIsNotNull($this$paneA, "$this$paneA");
      Function1 style$iv = (Function1)null.INSTANCE;
      String id$iv = (String)null;
      String classes$iv = (String)null;
      int var5 = false;
      ElementUtilKt.writeNormalElementStart($this$paneA, "div", id$iv, classes$iv, style$iv);
      int var7 = false;
      BodyContext.unaryPlus-impl($this$paneA, (CharSequence)"Panel A");
      ElementUtilKt.writeNormalElementEnd($this$paneA, "div");
   }

   public static final void paneB_caaDT_8/* $FF was: paneB-caaDT-8*/(@NotNull HtmlWriter $this$paneB) {
      Intrinsics.checkParameterIsNotNull($this$paneB, "$this$paneB");
      String id$iv = (String)null;
      String classes$iv = (String)null;
      Function1 style$iv = (Function1)null;
      int var5 = false;
      ElementUtilKt.writeNormalElementStart($this$paneB, "div", id$iv, classes$iv, style$iv);
      int var7 = false;
      BodyContext.unaryPlus-impl($this$paneB, (CharSequence)"Panel B");
      ElementUtilKt.writeNormalElementEnd($this$paneB, "div");
   }
}
Copy code
public final class DivDslKt$div$1 extends Lambda implements Function1 {
   public static final DivDslKt$div$1 INSTANCE = new DivDslKt$div$1();

   // $FF: synthetic method
   // $FF: bridge method
   public Object invoke(Object var1) {
      this.invoke(((BodyContext)var1).unbox-impl());
      return Unit.INSTANCE;
   }

   public final void invoke(@NotNull HtmlWriter $receiver) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
   }

   public DivDslKt$div$1() {
      super(1);
   }
}
g

Gunslingor

03/24/2020, 1:08 AM
hmm... so no html was really generated... the html gets generated from the JVM at runtime
s

spierce7

03/24/2020, 1:08 AM
yes. That’s what both of these libraries are doing
mine does it more efficiently though, because I’m just writing the items directly to a Stream or a String
g

Gunslingor

03/24/2020, 1:09 AM
same for kotlinx js/jvm... okay yeah, just wasn't was I was expecting.
s

spierce7

03/24/2020, 1:09 AM
they are creating objects that do the writing.
Let me show you how to create a DSL really fast
g

Gunslingor

03/24/2020, 1:09 AM
okay sure, I am in learning mode, thanks!
s

spierce7

03/24/2020, 1:12 AM
Here is a super simple mutable DSL for a person object:
Copy code
class Person(var name: String? = null, var age: Int? = null)

fun person(func: Person.() -> Unit): Person {
    val person = Person()
    person.func()
    return Person()
}

fun main() {
    person { 
        name = "John Doe"
        age = 30
    }
}
does that make sense? By using an extension function as a parameter, I can use it to build an object in a function. This is called a DSL - a Domain Specific Language. They are also called Groovy Style Builders. They are perfect for clearly and nicely building hierarchical data. A UI is just really heirarchical data. It’s a Parent View, with children that have children, that have children etc.
By adding another function to this DSL, I can add the ability to add children to this DSL
g

Gunslingor

03/24/2020, 1:18 AM
just looks like a normal person object with a function receiver for an arg to define what a person really means later (but we know what a person is so defining later seems lazy, lol)... but that isn't really DSL right???.... i mean there has to be a stage that produces something like <person data-name="Joe Doe" data-age="30">... or whatever, maybe person is a JS object in this case... I mean I see how I could write something to do that easy enough... in fact I think I could just output the createHTML functions to a file to get HTML templates if I wanted... but I think your telling be I don't need to in either jvm/js which makes sense a bit more know... I just know a lot about templates, not so much about byte code or js generating them.
s

spierce7

03/24/2020, 1:19 AM
no - this is technically an elementary DSL
I can make it more interesting, and more representative of heirarchical data by adding a simple
children
list to the object, and a
child
extension function:
Copy code
@DslMarker
annotation class PersonDsl

@PersonDsl
class Person(
    var name: String? = null, 
    var age: Int? = null,
    val children: MutableList<Person> = mutableListOf()
)

@PersonDsl
inline fun person(func: Person.() -> Unit): Person {
    val person = Person()
    person.func()
    return Person()
}

@PersonDsl
inline fun Person.child(func: Person.() -> Unit) {
    children += person(func)
}

fun main() {
    person {
        name = "Grandma"
        age = 75
        
        child {
            name = "Dad"
            age = 30
            
            child {
                name = "Baby"
                age = 1
            }
        }
    }
}
g

Gunslingor

03/24/2020, 1:20 AM
I mean... I know I have ran into issues in the past were my page was slow as shit because I was generating too much HTML with java and needed to preload the template kinda.
s

spierce7

03/24/2020, 1:20 AM
this DSL doesn’t build HTML. It just builds
Person
objects.
g

Gunslingor

03/24/2020, 1:21 AM
okay I understand what you mean I think... this is kinda a quasi language cause of the unique way you use the functions you defined.
s

spierce7

03/24/2020, 1:21 AM
exactly. That’s basically what I’m doing with my html library
only instead of building an object, I’m writing Strings to a StringBuilder or stream..
the html lib is considerably more complicated
but hopefully this demistifies what’s going on under the hood
Here is another simple DSL example that writes HTML to a
StringBuilder
:
Copy code
@DslMarker
annotation class HtmlDsl

@HtmlDsl
inline fun StringBuilder.html(func: StringBuilder.() -> Unit) {
    append("<html>")
    func()
    append("</html>")
}

@HtmlDsl
inline fun StringBuilder.body(func: StringBuilder.() -> Unit) {
    append("<body>")
    func()
    append("</body>")
}

@HtmlDsl
inline fun StringBuilder.div(func: StringBuilder.() -> Unit) {
    append("<div>")
    func()
    append("</div>")
}

fun main() {
    StringBuilder().html { 
        body { 
            div {
            }
        }
    }
}
g

Gunslingor

03/24/2020, 1:27 AM
Yes, I understand that better now... and I guess I feel better about all this... still a little concerned about JS generating all the HTML but I'm hoping it loads smartly so to speak... I mean I recall traditionally its HTML is loaded first, then style tags then script tags in order and all that is very important to do in good order so load experience is good... not sure if the JS cas considered that, need to research the visitTag function it uses.
Yeah, I think i get it.
s

spierce7

03/24/2020, 1:28 AM
to be clear - I haven’t done any html rendering in JS
I render on the server
g

Gunslingor

03/24/2020, 1:28 AM
its a string in memory right
ok
yeah, I'm talking kotlinx js
s

spierce7

03/24/2020, 1:29 AM
I’m not even sure if I’ve enabled my lib to compile for js - although I could release a js version in 10 minutes if you wanted one
g

Gunslingor

03/24/2020, 1:29 AM
doing it on the client side can be very useful, like for adding rows to an existing table.
s

spierce7

03/24/2020, 1:29 AM
sure - I agree
but what you’ll be doing is inserting a String of html into the dom
I don’t know how well browsers perform with that
g

Gunslingor

03/24/2020, 1:30 AM
or a Node into another Node so I've learned... appendElement vs appendChild
That was my concern...
s

spierce7

03/24/2020, 1:30 AM
these libs don’t output elements or children though
they only output Strings
g

Gunslingor

03/24/2020, 1:31 AM
I kinda wanted to keep all fronted, templates with the front end...
s

spierce7

03/24/2020, 1:31 AM
I would only do something like that behind an login
g

Gunslingor

03/24/2020, 1:31 AM
yeah, they can take strings or elements/objects, took me a while to figure that out
s

spierce7

03/24/2020, 1:32 AM
if this is something that you want to rank well on google, and other things, you won’t get fantastic performance from client-side rendering
g

Gunslingor

03/24/2020, 1:32 AM
I agree with that... I mean this software is such that it will need an online mode where it gets real time pages/updates from a server and offline mode where the server is on the client... shit gotta run
s

spierce7

03/24/2020, 1:32 AM
not just with kotlin-js, or either of these libs. Client side rendering period is heavy
g

Gunslingor

03/24/2020, 2:45 AM
yeah, google results are not desired for this.... this will be logistics type software but has to use the browser, private networks
5 Views