Repository navigation
Making parent template fragments optional #248
Description
Activity
Hey there, so given what you've described, I do agree that the workaround is a lot. The reason however is that you're going somewhat against the grain of what the layout dialect provides, which is an inheritance-based approach to creating templates (as opposed to a compositional one). As such, the layout dialect follows how inheritance would work in a language like Java, eg: if you had
class Awith a method you could override, then createclass B extends Abut don't override that method, the base method isn't gonna suddenly disappear, so neither will any fragments in the template.However, I had a play with some other approaches and found something that might be useful to you instead of having to put
th:remove="all"/th:remove="none"all over the place.Thymeleaf comes with a special tag,
<th:block>, which acts a lot liketh:remove="tag". By replacing thedivs with that in your base template, and then removing the "Optional 1" / "Optional 2" text in the tags, I was able to get something like that 'remove if not overridden' behaviour that you were describing. Here's a Thymeleaf test file I was working with which shows the input, template, and result:%TEMPLATE_MODE HTML %INPUT <!DOCTYPE html> <html layout:decorate="~{layout}"> <body> <div class="part2" layout:fragment="optional-2">This from the child template</div> </body> </html> %INPUT[layout] <!DOCTYPE html> <html> <body> <div class="header-container"> <th:block class="part1" layout:fragment="optional-1"></th:block> <th:block class="part2" layout:fragment="optional-2"></th:block> </div> </body> </html> %OUTPUT <!DOCTYPE html> <html> <body> <div class="header-container"> <div class="part2">This from the child template</div> </div> </body> </html>
How this works is that the layout dialect will replace the
th:blockin the base template with the element from the matching fragment in the input file. If no matching fragment exists, then it'll stay ath:blockand so Thymeleaf will remove it.If the text within the elements is important, then maybe writing a custom element like
th:blockthat acts more liketh:remove="all"would be the way to go. Anyway, I hope this helps you achieve what you need, or at least point you in a direction towards it.Thanks for that. I'd seen the
<th:block>tag, and I use it for some other include sections elsewhere in the page. The other includes are Javascript and additional stylesheet elements so they're "hidden". The reason I've been banging my head so hard on this, though, is that one of the things that is really good about Thymeleaf with layout inheritance is that I can modify and check my styling simply using the template. I have my template with the optional sections all wrapped with divs like they would be in the processed page and I can check that my styling works without having to compile the app, fire up the application container, hit a web page, log in, then click through to a sub-page. The<th:block>option doesn't allow for that.And to come back on the inheritance point, If I declare
class Awith methodsomeMethod()that returns a long , complicated string, then I can createclass Bwhichextendsclass A. It can then replacesomeMethod()with its own version that returns a different long, complicated string, but it could also return null, or an empty string.The conceptual difference between how I view it drops back to where the output of the "method" fits. Thymeleaf, and the layout dialect, replace the
<div>and its content with another<div>and its content. There is no way to specify "null" here, and I think having that ability would make the whole thing more flexibleThere is no way to specify "null" here, and I think having that ability would make the whole thing more flexible
Given
<th:block layout:fragment="..."></th:block>is sort of Thymeleaf's version of "null" when put in the child template - because the layout dialect would replace the parent element (th:blockremoves the entire element) and content (which is empty) - would using that in your child templates be a better workaround than theth:remove's everywhere? (Yes, it is still verbose, but just like your inheritance example of replacing the base method with one that returns null, you still have to write it out in every child class to replace that base method which is similarly verbose.)Anyway, just offering some more ideas that could work as better analogs of the 'replace base implementation with null' that you're trying to achieve. I'll have a think about adding an option to the layout dialect that replaces unmatched fragments with nothing as a shortcut/convenience for putting empty
th:blocks everywhere.That option is actually more work, because I'd have to specify a blank
<th:block>in every child template where I didn't want the element to appear. Havingremove="all"in the parent template and then overriding it withremove="none"means I only have to include the fragment in child templates that need it.
I have a base template that drives multiple child pages. These pages all share a common header section, but then there are two sections just below that header that may or may not be required in the child.
If my child template does not have a
layout:fragmentcalled "optional-1", then I want thedivwith class "part1" to not be present in the output. So:Should result in:
... with the "Optional 1"
divcompletely removed.The only way I can get this to work is to add a
th:remove="all"to the base template, and a correspondingth:remove="none"to all of the child templates (becauseth:remove="all"without the corresponding "none" in the child template blows away the replaced tag completely)Firstly, am I missing something obvious?
Secondly, am I weird for finding the need to add an effective manual override in all the child templates feels wrong and inefficient?