I am trying to migrate some functionality from JQuery to JSF and I am getting strange results.
Basically it is a page where you can navigate links (imagine like a Windows Explorer on the Web). Folders have a link and leafs don't. To construct this, it is used a private List<Element> list;
, which is refreshed accordingly. So when the user clicks on a "folder", a new list
is created with the contents of that folder.
The problem is that I am getting strange results. Sometimes I navigate a link and it is refreshed correctly, but in some cases it behaves like the page is reloaded (but it is not) and the list
is reset to its initial values (?). For example, I navigate to "name2" and get the right things. But then I go to "name2b" and I instead of the leafs I get "name1", "name2" and "name3", which are only set in the init()
method. I don't know how this happens, but exploring the source code it seems that the c:foreach
structure is messing with the ids and an old ID is rendered in the page (note that I have tried ui:repeat
with same incorrect results).
How can I fix this?
This is the simplified code:
<f:metadata>
<f:viewAction action="#{controller.init}" />
</f:metadata>
...
<h:panelGroup id="visor" layout="block">
<ul>
<c:forEach items="#{controller.list}" var="element">
<li>
<ui:fragment rendered="#{element.folder}">
<h:form>
<h:commandButton action="#{controller.navigate(element.name)}" type="submit" value="#{element.name}" >
<f:ajax render="visor" execute="@this" />
</h:commandButton>
</h:form>
</ui:fragment>
<ui:fragment rendered="#{not element.folder}">
<span>#{element.name}</span>
</ui:fragment>
</li>
</c:forEach>
</ul>
</h:panelGroup>
And the controller
@Named(value = "controller")
@ViewScoped
public class MyController implements Serializable {
private List<Element> list;
public void init() { //Called when loading the page
list = new ArrayList<>();
Element e1 = new Element("name1", true); //name and isFolder?
Element e2 = new Element("name2", true);
Element e3 = new Element("name3", true);
list.add(e1);
list.add(e2);
list.add(e3);
}
public void navigate(String name) {
list = new ArrayList<>();
if (name.equals("name1")) {
Element e1 = new Element("name1a", true);
Element e2 = new Element("name1b", true);
Element e3 = new Element("name1c", true);
list.add(e1); list.add(e2); list.add(e3);
} else if (name.equals("name2")) {
Element e1 = new Element("name2a", true);
Element e2 = new Element("name2b", true);
Element e3 = new Element("name2c", true);
list.add(e1); list.add(e2); list.add(e3);
} else {
Element e1 = new Element("leaf1", false);
Element e2 = new Element("leaf2", false);
list.add(e1); list.add(e2);
}
}
//....
EDIT
My problem seems to be described in here.
I have to investigate if this question can bring some light to the subject.
Apparently it is a well-known thing that my structure cannot be dynamically updated in JSF. The solution I have to make is to use a h:datatable
instead of a <ul>
list (this is one thing I really dislike of JSF, but I am not sure how to render a list with basic JSF - instead of a table, that I can dynamically update.
The Ajax render
attribute had to specify the form
id.
<h:form id="visor">
<h:dataTable value="#{controller.list}" var="element">
<h:column>
....
<h:commandButton action=.... >
<f:ajax render="visor" execute="@this" />
</h:commandButton>
....
</h:column>
</h:dataTable>
</h:form>
Edit: The UL
also worked, as long as I render the form and the form contains everything