This is an example to emulate the benefits of fragments using the Java FreeMarker templating library. The main motivation to demonstrate this feature was to support use cases like htmx fragments.
In order to render conditionally, macros are used in a component-like style (inspired by React). By taking advantage of FreeMarker macros being defined at parse time, not at process time, we can call them before their definition in the file.
Example with macro definitions omitted
<!DOCTYPE html>
<html>
<@Head />
<body>
<@Menu />
<@HeroBanner />
<@Sidebar />
<@MainContent />
<@Footer />
</body>
</html>
<<< macros go here >>>
By using specific configuration,
we can optionally specify a fragment identifier as part of the view name in the Controller.
e.g. return "myView :: MyFragment"; where the template is myView.ftlh and the macro to invoke is MyFragment.
Including the fragment identifier will cause the matching macro in that template to be invoked and only the output
generated by that macro will be rendered.
When using the FullyAutomatic strategy, nothing special is required from the template or the Controller. If a fragment
identifier is specified, then only the model attributes used by the selected macro are required (i.e. we avoid
evaluating the full template).
Optionally (disabled by default), the code can automatically convert kebab-case and snake_case identifiers to match
macros with UpperCamelCase/PascalCase names.
e.g. return "view :: my-fragment"; to invoke the macro MyFragment.
See IMPL_NOTES for details of manual fragment handling as well as ideas about how to introduce limited automation for them.
This is built using Spring Boot and so to start the server, either:
- in your IDE, run the
mainmethod inFreeMarkerFragmentsApplication - as standalone:
- either use
./mvnw spring-boot:run - or, build the project using
./mvnw clean packageand then run the jarjava -jar target/fragments.jar
- either use
Pages
The same pages exist for both implementations under /auto and /manual.
Simple page and a fragment of it (the article text):
http://127.0.0.1:8080/auto
http://127.0.0.1:8080/auto/fragment
http://127.0.0.1:8080/manual
http://127.0.0.1:8080/manual/fragment
Page with a table and also a single row as a fragment.
A single row's HTML would be useful when the client wants to add a completely new row.
You can easily get the same HTML as for when you're rendering the whole table, without having to specify it more than
once and then needing to keep both versions in sync.
http://127.0.0.1:8080/auto/table
http://127.0.0.1:8080/auto/table/row
http://127.0.0.1:8080/manual/table
http://127.0.0.1:8080/manual/table/row