How to render menu using thymeleaf recursively
Pregunta
I would like to render HTML menu using ul/li list. I've got this class structure:
public class MenuItem {
private String name;
private MenuItem parent;
private List<MenuItem> children;
public MenuItem(String name,List<MenuItem> children) {
this.name = name;
this.children = children;
for (MenuItem child : children) {
child.parent = this;
}
}
public MenuItem(String name) {
this.name = name;
}
public String getName() {
return name;
}
public MenuItem getParent() {
return parent;
}
public List<MenuItem> getChildren() {
return children;
}
}
As you can see it's typical tree structure, in which one root element contains reference to its children and they have reference to theirs and so on.
Now I would love to render structure like this:
<ul>
<li>Item 1
<ul>
<li>Item 1.1</li>
<li>Item 1.2
<ul>
<li>Item 1.2.1</li>
<li>Item 1.2.3</li>
</ul>
</li>
<li>Item 1.3</li>
</ul>
</li>
<li>Item 2</li>
</ul>
How can I do that with thymeleaf? I'm easy if I have to use other technologies like Tiles, Layout or anything else.
EDIT: I tried parameterized include/replace but with no luck. Parameters passed through are converted to String and can't be used for another level of recursion. See https://github.com/ultraq/thymeleaf-layout-dialect/issues/12
Thank you very much,
Frank
Solución
Try this:
Create a fragment for single menu item as menuFragment.html
<html>
<section layout:fragment="menu">
<ul>
<li th:each="menuItem : ${menuItemsList}" th:text="${menuItem.name}">
<section layout:include="@{path/to/folder/menuFragment} :: menu" th:with="menuItemsList=${menuItem.children}"></section>
</li>
</ul>
</section>
</html>
Include the fragment in your menu file as
<section layout:include="@{path/to/folder/menuFragment} :: menu" th:with="menuItemsList=${rootMenuItemAsList}"></section>
rootMenuItemAsList
is a list with your parent menu
Hope this help will you
Otros consejos
I tried this and it seems to me that the line:
<li th:each="menuItem : ${menuItemsList}" th:text="${menuItem.name}">
<section layout:include="@{path/to/folder/menuFragment} :: menu" th:with="menuItemsList=${menuItem.children}"></section>
will overwrite the include (inside the li - tag) because the th:text attribute does exactly that: overwrite the content inside the li block.
I got this to work with the following:
<li th:each="menuItem : ${menuItemsList}">
<span th:text="${menuItem.name}">prototype text...</span>
<section layout:include="@{path/to/folder/menuFragment} :: menu" th:with="menuItemsList=${menuItem.children}"></section>
</li>
Hope this helps...
In my main I include:
<section th:include="@{categoryFragment} :: category" th:with="maincategories=${categories}"></section>
and as Fragment i use:
<section th:fragment="category">
<ul>
<li th:each="cat : ${maincategories}">
<span th:text="${cat.categoryName}">Main Cat</span>
<section th:include="@{categoryFragment} :: category" th:with="maincategories=${cat.getChildren()}"></section>
</li>
</ul>
</section>
This works absolutly fine for me.