Closed nedtwigg closed 1 year ago
I've been using Tailwind with JTE. I use DaisyUI component library. So one thought is using a component library with Tailwind reduces the classes you add within the HTML
Otherwise I've been rendering repeated sections in a loop, so the markup isn't repeated, or else creating a separate template file. It doesn't feel that onerous to me.
I definitely think JTE is the best game in town as far as JVM templating. But if I spend an afternoon in Typescript world, the extra friction in JTE becomes apparent.
There is a loop version of my example, maybe something like
@param SalesState state
<div class="mt-6">
<h2 class="text-base font-semibold leading-7">Recent usage</h2>
<dl>
@for (entry : Map.of(
"Past 7 days:", SalesState.RecentUsage.Period.WEEK,
"Past 28 days:", SalesState.RecentUsage.Period.MONTH,
"Past 365 days:", SalesState.RecentUsage.Period.YEAR).entrySet)
<div class="flex justify-start">
<dt class="w-[105px] text-sm font-medium leading-6 text-gray-900">${entry.getKey()}</dt>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0 w-[200px] flex justify-between">
<a href="${state.getUsage().getUrls().get(SalesState.RecentUsage.Period.WEEK)}" class="font-medium text-indigo-600 hover:text-indigo-500">${state.getUsage().getCount().get(entry.getPeriod())}</a>
</dd>
@endfor
</dl>
</div>
but it feels like code golf, and it doesn't work for the general case. It's certainly not a bug that JTE is strictly one-file-per-tempate, but I think it would be a great added feature if you could easily break up a template file with inline subfunctions.
Here's an idea (I haven't use this approach much - so I don't know how convenient it is in practice): Using lambdas to define sub-templates.
Example:
!{Function<String,Content> component = (s) -> @`<div>${s}</div>`;}
@for(entry: entries)
${component.apply("foo")}
@endfor
@nedtwigg : Does this help with your use-case?
Oh, this is an awesome idea, thanks so much @chkl. We tried this and went through two evolutions:
v1: we happen to be using jOOQ, so we already have org.jooq.Function1
(and 2, 3, 4, 5, etc.) so we have a ready-made type for every reasonable function.
v2: we tried switching to kte
, and Kotlin's lambda syntax makes it absolute butter
.jte
declare: !{org.jooq.Function2<String, String, gg.jte.Content> li_link = (String href, String content) -> @` ...
usage: ${li_link.apply(Admin.URL_impersonate, "Stop impersonating")}
.kte
declare: !{val li_link = { href: String, content: String -> @`
usage: ${li_link(Admin.URL_impersonate, "Stop impersonating")}
With the .jte
syntax, it's onerous but doable. With the .kte
syntax, I think this issue is totally solved. Thanks a ton!
Tailwind CSS is a popular CSS framework. It uses a lot of verbose inline classes rather than custom CSS. Part of the motivation behind Tailwind is that since everyone is rendering HTML from templates, use those HTML templates to do reusable styles instead of CSS. Don't have a button class with a bunch of CSS, have a button HTML template with a bunch of inline styles.
Parts of this work great with JTE, but parts don't. The big thing missing is that it's too verbose to create an entire file for every template. It would help a ton if we could declare smaller, possibly private templates within the bigger JTE template. For example, here's a template we have
It would be so nice if we could do something like this:
It doesn't really matter if it's private or not, and the syntax doesn't matter. But being able to easily add a little "subroutine" inside template files would really be a game changer.
Besides Tailwind, a lot of the new frontend tech (AlpineJS is another one) is perfect for a system like JTE, because they let you build great user experiences from "static" HTML. But they assume that the HTML is being written from e.g. TSX files, where you can easily whip up a quick function that spits out HTML. JTE is very close to being able to do that also...