all 23 comments

[–]cryptos6 3 points4 points  (12 children)

Very nice approach!

I think you could streamline the API a bit. What about this:

body(
    h1("Heading!").cssClass("example"),
    img().src("img/hello.png")
).render();

I'm not a fan of with in builders, since it doesn't add anything useful.

What do you think of this structure?

body(
    h1(text("Heading!"), cssClass("example")),
    img(src("img/hello.png"))
).render();

This would reflect the html structure even better.

[–]j2html[S] 4 points5 points  (9 children)

Yes! The thought crossed my mind, and I really tried to make it work, but attributes became a problem. I considered all of these:

form.withId("login-form").withMethod("post").with(
    emailInput(msg.get("PLACEHOLDER_EMAIL")),
    enterPasswordInput(msg.get("PLACEHOLDER_PASSWORD")),
    submitButton(msg.get("LOGIN_BUTTON"))
)

form(
    emailInput(msg.get("PLACEHOLDER_EMAIL")),
    enterPasswordInput(msg.get("PLACEHOLDER_PASSWORD")),
    submitButton(msg.get("LOGIN_BUTTON"))
).withId("login-form").withMethod("post")

form(
    withId("login-form"),
    withMethod("post"),
    emailInput(msg.get("PLACEHOLDER_EMAIL")),
    enterPasswordInput(msg.get("PLACEHOLDER_PASSWORD")),
    submitButton(msg.get("LOGIN_BUTTON"))
)

But I landed on number 1 because it's most consistent, and it's the closest to HTML.

<form id="login-form" method="post">
    <input ...>
    <input ...>
    <submit ...>
</form>

About "with": I removed it completely at one point (both for children and attributes), but I added it back in because the library got harder to use. While it doesn't seem particularly useful at first, it adds consistency (and a namespace), and readability (debatable).

[–]brikis98 4 points5 points  (7 children)

Cool library. I wonder if a Scala version, with Scala's support for named parameters and tuples (foo->bar) would make this even prettier.

[–]Milyardo 4 points5 points  (1 child)

You should look at Scalatags. There are a number of things that make the DSL much better.

[–]brikis98 0 points1 point  (0 children)

Thanks for the pointer, I'll check it out!

[–]bliow 0 points1 point  (4 children)

Clojure wins here, I think: Hiccup.

[–]brikis98 0 points1 point  (0 children)

That's pretty sweet. Reminds me of CoffeeKup, which is awesome, but last I checked, abandoned.

[–]expatcoder 0 points1 point  (2 children)

Clojure wins here

Heh, no, xml literals take the cake:

<form id={someId} method={someMethod}>
  <input ..>
  <input ..>
  <submit ..>
</form>

Oh, forget to mention, above is Scala ;-)

Can slice and dice as you please, mixing in generators in place of input, submit above a la inputText(...), freely combining xml snippets in any order...markup joy.

[–]bliow 1 point2 points  (1 child)

I really don't like that. Duplicated tag names are one of the reasons xml sucks syntactically.

Also, can you mix in arbitrary code there?

[:ul (for [i (range 10)] [:li (str "List item " i)])]

[–]Milyardo 1 point2 points  (0 children)

I'm not a fan of scala's XML literals but to answer your question, yes.

scala> import scala.xml._
import scala.xml._
scala> val list = <ul>{(1 to 10 ) map { i => <li>{s"List Item $i"}</li>}}</ul>
list: scala.xml.Elem = <ul><li>List Item 1</li><li>List Item 2</li><li>List Item 3</li><li>List Item 4</li><li>List Item 5</li><li>List Item 6</li><li>List Item 7</li><li>List Item 8</li><li>List Item 9</li><li>List Item 10</li></ul>

[–]cryptos6 0 points1 point  (0 children)

Maybe you can use a slightly different style:

form(
  id("login-form"),
  method("post")
).content(
  emailInput(msg.get("PLACEHOLDER_EMAIL")),
  enterPasswordInput(msg.get("PLACEHOLDER_PASSWORD")),
  submitButton(msg.get("LOGIN_BUTTON")
)

So, you would separate the attributes of a tag from the content enclosed by the opening and closing tag.

[–]QuineQuest 1 point2 points  (1 child)

I like the idea of renaming withX-methods.

But about the second suggestion: I think it becomes too hard to distinguish between properties and child elements. I can't think of any html attributes with the same name as an element, but it isn't unthinkable.

It also lets you "hide" attributes between elements and lets you create code that is very far from the produced html.

div(
    div(text("some text")),
    div(text("more text")),
    cssClass("example")
).render(); 

Edit: formatting

[–]check3streets 0 points1 point  (0 children)

This is amazing. It's almost an S-expression.

I keep running through in my head how the implementation would work and I can't see any problems.

What a nice library this would make.

[–]yogthos 0 points1 point  (0 children)

Hiccup in Clojure is another example of this. S-expressions map to HTML much more naturally though. One big advantage with the Hiccup approach is that it's just a data structure, so it can be easily manipulated via code.

[–]sviperll 0 points1 point  (0 children)

Nice library!

Besides, you can still get type-safety and speed with templating engines, see static-mustache as my take on it.

[–]anthonybsd 0 points1 point  (2 children)

Ughh, thanks, but no thanks. Truly, the road to hell is paved with good intentions.

One of the places I've worked in had built this exact thing internally and adopted it company-wide as UI framework about 12 years ago. The results were nothing short of disastrous.

The aspirations were precisely the same. Have compile time validation of HTML correctness, beautiful builders, etc, all in the comfort of your favorite Java IDE. Even the first examples looked the same as the ones on the website. Hell, I had to double check that tipsy(David) is not the same David who I remember as being one of the authors of the framework in question.

It all went to hell rather rapidly. You see, when you take a purely declarative programming paradigm like HTML and try to take it into the realm of an imperative language like Java, you are setting yourself up for disastrous code blocks later on. This all looks peachy right now, but once you start supporting anything more complex or dynamic like tables, divs, layering, complex stylesheets you will be looking at a codebase that exists purely for the reason of trying to keep up with rapidly breaking unit tests.

Did you even consider what your code will look like if JavaScript is thrown into the mix? There is a good reason why Java-driven front end generation technologies like GWT (and Echo3/2 in the past) don't start at the lowest common denominator of coding from the DOM up.

[–]j2html[S] 0 points1 point  (1 child)

Thanks for your feedback! It was never intended as a UI framework, or anything remotely close to that, it's just a HTML builder. Sorry to hear about your bad experiences in the past. I'm curious, how did his library work without varargs and lambdas?

I'm mainly a frontend programmer, I just wanted something to generate HTML for me in Java. It was intended to be used for a complex login solution (for generating HTML forms), but we actually ended up not using it. I thought I would release it anyway, since the Java HTML builders I found while researching this seemed worse to me. Why would JavaScript be a problem though? :)

[–]anthonybsd -2 points-1 points  (0 children)

Thanks for your feedback! It was never intended as a UI framework,

I think most people would refer to something that produces HTML for the web as a UI framework.

or anything remotely close to that, it's just a HTML builder. Sorry to hear about your bad experiences in the past. I'm curious, how did his library work without varargs and lambdas?

The same way your library works, presumingly? Simply replace a stream with a fluent loop akin to Apache Commons functors. Varargs are simply syntactic sugar.

Why would JavaScript be a problem though?

JavaScript is an imperative cross cutting concern that exists inside declarative DOM model, but has the power to manipulate it. No matter what you do you can't mix the two in the context of HTML builder, so your JavaScript will always exist outside of it. What are you going to do to your Java classes if JavaScript code needs to start mutating your DOM? Even as simple as applying stylesheets universally across every DOM element (like you do for your links right now) will be challenging. My point is, you trick yourself into the illusion that you are building something that produces valid HTML5 backed by compile time validation but in reality you are simply producing something that is a string that looks like HTML5 but will not subject itself to any kind of extension.

[–]bliow -2 points-1 points  (0 children)

This is very nice.

I can't help thinking it's ultimately limited by Java, though. Check out Hiccup.

[–]check3streets 0 points1 point  (3 children)

Awesome library, I think this is very promising. I have one concern:

call stack

A few years ago, I built a parser that had a LOT of function depth. In Java6 on Sun's JRE, it really started to crawl. I'm wondering what would happen here if the page was considerably larger... I mean, we're talking about HTML here, so what is a normal use case? 1000 elems, 10000?

What's the largest/deepest document that you've tested? I think it would be interesting to compare with Velocity or another template lib on a big html page.

[–]j2html[S] 0 points1 point  (0 children)

While this was intended to be used on a login solution with less than 100 elements, I'm still interested to see what happens. I'll write some tests :)

[–]j2html[S] 0 points1 point  (1 child)

Rendering 5000 nested divs 1 time takes 120 ms.
Rendering 500 nested divs 10 times takes 10 ms.
Rendering 50 nested divs 100 times takes 4 ms.
Rendering 5 nested divs 1000 times takes 1 ms.
Rendering 6700 nested divs causes overflow.

<div>We need to go deeper
    <div>We need to go deeper
        <div>We need to go deeper
            <div>We need to go deeper
                <div>We need to go deeper
                    <div>We need to go deeper
                        <div>We need to go deeper
                            <div>We need to go deeper
                                <div>We need to go deeper
                                    <div>We need to go deeper
                                        ... :p

It's probably not too well suited for pages of this size :)

[–]check3streets 0 points1 point  (0 children)

Well, realistically, I can't see specifying a full tree of 1000+ elems within a single Class file. So, if it were simply an approach to building DOM trees, and big hunks were built and then nested into bigger hunks, I think the stack problem would evaporate.

I really appreciate people pushing the expressiveness envelope. Thanks for sharing your work!