How do I do framgments with JStachio? #191
-
I just discovered JStachio, and I really like what I see so far! However, one important feature for me is "fragments". This seems the opposite of "layouts" (that I see in the docs), and will allow you buddy, the web designer (who knows nothing about Java/JStachio) to author your html/css templates and work in parallel with you, the web java developer. Not only this leads to a nice separation of labour, but more importantly - helps you avoid growing many small templates into a pile of incomprehensible, interconnected mess. The "fragments" feature is explained here and is present in both Qute and Thymeleaf So, how do I do "fragments" in JStachio? |
Beta Was this translation helpful? Give feedback.
Replies: 9 comments 23 replies
-
I haven't done or seen this technique for awhile. I believe PJAX (the precursor to libraries like htmx and unpoly) had this technique and that was the last time I did something like this. Because you can't send URI fragments as parameters the Javascript frameworks like htmx usually send HTTP headers or query parameters then the web frameworks and or templating languages have to do special stuff to render only the fragment. This is because on regular non ajax web 1.0 style page request you need to deliver the entire HTML page. What I recommend is to just send the entire page back on every request and let htmx pull a This isn't obvious and you might not believe me but is often more efficient to always send the entire page because you only have to cache the entire page instead of the page and a fragment. Even if you don't do caching with things like gzip and http2 it really isn't that much faster sending a fragment of html vs an entire html page (especially compared to assets). Browsers are very fast at parsing HTML. Furthermore sending a fragment of HTML is not proper HTML and it is disingenuous if the server sends That being said I can see how it is easier to debug if you are using browsers developer tools you would just see the response of the fragment and the htmx code is also easier as well as you just say swap everything. Regardless the hack implementation of this is basically just having a flag to tell the outputstream to ignore content. So you can do this across the board in many templating languages including jstachio by using a custom outputstream and then getting access to that stream to tell it its "on" or "off". You can do this with some threadlocal magic. Let me see if I can pump out a hack to show this technique this weekend. And of course you could always do the incredibly naive and simple solution of just using header footer conditions but I'm sure that is not desirable. Anyway thanks for the great question and I hope soon to find a good solution for you! |
Beta Was this translation helpful? Give feedback.
-
Copied previous reply here so it is top level: I completely understand the need and desire for a single template that acts like many templates. I think it is highly desirable as I agree nobody wants fuck tons of partials (mustache partial templates are basically just includes). Otherwise I would completely dismiss this request :) There are basically two options (ignoring what is actually easily available but what is possible in the context of JStachio's model): Option 1We allow reference to a subset of a template: @JStache(path = "base.mustache#my-fragment")
public record SomeChild(){}; @JStache(path = "base.mustache")
public record Base(SomeChild someChild){}; But then in your request handling you would have to know to use So there is not just integration with JStachio but the web framework as well assuming you want the same endpoint to generate the full content (that was my web 1.0 argument). Option 2You always use and render base: @JStache(path = "base.mustache")
public record Base(SomeChild someChild, SomeFragmentChoiceThing fragment){}; And then just tell it to omit output when not in the fragment. This is doable today but with lots of ceremony and hacks. Anyway I can put together a solution using JStachio today for option 2. |
Beta Was this translation helpful? Give feedback.
-
I guess my question is do you want the same endpoint to generate the full HTML or a subset or do you really just want a way to reference a subset of a template as though it were a regular template file? |
Beta Was this translation helpful? Give feedback.
-
Option 2 and just sending the entire HTML response every time is super easy just using enums as fragments. Let us use htmx example of In JStachio you would make one class for all @JStache("/contacts/details.html")
public record ContactDetails(Contact contact, @Nullable Fragment fragment){} // I'm just using records for brevity.
public enum Fragment {
ARCHIVE_UI,
SOME_OTHER_FRAGMENT,
UNARCHIVE_UI;
} details.html as mustache: The above is assuming you largely generate the same HTML as For You can think of Fragments in this case as "mode" or "sub template". |
Beta Was this translation helpful? Give feedback.
-
BTW I'm curious where you saw JEP 430 doing layout other than you know just manually doing it with Java? It is interpolation and not full templating. I'm fairly sure JEP 430 cannot loop through items nor does it have conditions. If it does can you show me an example. I have been meaning to play with it more but haven't had the time. |
Beta Was this translation helpful? Give feedback.
-
OK I am putting in fragment support and I think you will like how it works. I think will be surprisingly elegant. #193 Let us say we have a template like You can now do: @JStache("/contacts/details.mustache#archive-ui")
public record ContactDetails(Contact contact){} JStachio will see the template as: I love the happy coincidence that sections have The question is if we should support fragments within fragments.
I'm inclined to say no and just pick the first section with name of the fragment. Thoughts? Thank you so much for bringing this up. I think it will be kick ass and I see lots of interesting use cases with Lambdas as snippet/partial context is a pain point. |
Beta Was this translation helpful? Give feedback.
-
fwiw, I think that is a great syntax for both declaration and use of fragments one argument for supporting nested fragments is situations like this: Say you have a table and that table can be refreshed for some reason (e.g. sorting by clicking on the header), but then, within that table, you have a row-specific operation (e.g. inline editing) that targets only the row. In that case it would be nice to declare an outer section for the table and an inner section for the row. Not a major use case, but wanted to give you one to think about based on my experience w/ htmx hope that's useful, and great job on this feature! |
Beta Was this translation helpful? Give feedback.
-
@1cg and @hrstoyanov Fragments are in 1.2.0 I actually do some unique whitespace handling to follow Mustache whitespace philosophy. https://jstach.io/jstachio/io.jstach.jstache/io/jstach/jstache/JStache.html#_fragments @jgonggrijp might find this whole fragments thing interesting. I'm not sure its worthy of adding to the spec but nonetheless it is great how well it appears to work for Mustache syntax. |
Beta Was this translation helpful? Give feedback.
-
I think I might like some kind of nesting. Not sure how this plays out because my example has an iterable in it, whereas the examples above don't. E.g. suppose I'm rendering a list of "todos": <!-- index.mustache -->
<html>
<body>
<ul>
{{#todos}}{{<todo}}{{/todo}}{{/todos}}
</ul>
</body>
</html> I want to re-use the entire <!-- index.mustache -->
<html>
<body>
{{#hasTodos}}
<ul>
{{#todos}}{{<todo}}{{/todo}}{{/todos}}
</ul>
{{/hasTodos}}
</body>
</html> with a main model @JStache(path = "index")
record Page(TodosContainer hasTodos) {} @JStache(path = "index#hasTodos")
record TodosContainer(List<Todo> todos) {} Then I can render a <!-- index.mustache -->
<html>
<body>
{{#hasTodos}}
<ul>
{{#todos}}<li id="{{id}}">{{title}}</li>{{/todos}}
</ul>
{{/hasTodos}}
</body>
</html> So I would need something like @JStache(path = "index#hasTodos#todos")
record Todo(String id, String title) {} Does that make sense? Could it ever work, given that "todos" is iterable? |
Beta Was this translation helpful? Give feedback.
@1cg and @hrstoyanov Fragments are in 1.2.0
I actually do some unique whitespace handling to follow Mustache whitespace philosophy.
https://jstach.io/jstachio/io.jstach.jstache/io/jstach/jstache/JStache.html#_fragments
@jgonggrijp might find this whole fragments thing interesting. I'm not sure its worthy of adding to the spec but nonetheless it is great how well it appears to work for Mustache syntax.