You are correct. Tags like <ui:repeat>
and <p:repeat>
are invoked during rendering time. There is no guarantee that they will work with all components and it highly depends on how they are designed. The parent component has to have support for dynamic rendering and support these tags - otherwise you are out of luck.
With that said, you can get around this by using JSTL, which will effectively create your ribbon during document build time instead of during rendering time. Using your example as a base this would be implemented like this (Note that this example has dependencies on Lombok and Apache Commons);
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui" xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<h:head>
<title>Dynamic Ribbon Test</title>
</h:head>
<h:body>
<h:form>
<p:ribbon>
<c:forEach var="tab" items="#{dynRibbonBackingBean.tabs}">
<p:tab binding="#{tab}" />
</c:forEach>
</p:ribbon>
</h:form>
</h:body>
@Data
@Named
@ViewScoped
public class DynRibbonBackingBean implements Serializable {
private List<Tab> tabs;
@PostConstruct
private void init() {
tabs = new ArrayList<>();
for (int i = 0; i < RandomUtils.nextInt(5, 15); i++) {
final Tab t = new Tab();
t.setLoaded(true);
t.setTitle(RandomStringUtils.randomAlphabetic(10));
final RibbonGroup r = new RibbonGroup();
r.setLabel(RandomStringUtils.randomAlphabetic(10));
r.setParent(t);
tabs.add(t);
}
}
}
Here we use the <c:forEach>
tag instead to iterate and build the dynamic ribbon during document build time. In this example, I am randomizing the ribbon completely during @PostConstruct
, so you can reload the page and watch how the tabs of the ribbon change on each page load (both in count and title).
You can also gain some additional insight into this topic by referring to this previous Q/A here on the site;
When <ui:repeat> works for X but <c:forEach> works for Y