0

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.

user1156544
  • 1,725
  • 2
  • 25
  • 51
  • Start by **not** doing `type="submit"` on the commandbutton. And how and when is the init called? Should it not just be a `@PostConstruct` annotated method. Please try to create a [mcve] instead of having us guess. Oh and check http://showcase.omnifaces.org/components/tree, might be very intersting And **debug**... check if things are unexpectedly called... – Kukeltje Jun 07 '18 at 12:57
  • What is the reason for not `submit`? I have done this in other places and it always worked fine. I will update for the init(). I have also debugged and the calls are all correct and the size() of the list too. But when I get the error, there is no call to "navigate()" – user1156544 Jun 07 '18 at 13:01
  • 1: it is the 'default', 2: In combination with ajax I've sometimes seen strange things (jsf <2.2 but never the less).. Oh now I noticed you use ajax and (think) update the page with the `c:foreach`. That won't work. You cannot update a part that is rendered via c:foreach... see https://stackoverflow.com/questions/3342984/jstl-in-jsf2-facelets-makes-sense (read all >25 upvote Q/A on JSF and remember their existence for the future.... really helpful. Or start at http://jsf.zeef.com – Kukeltje Jun 07 '18 at 13:14
  • Thanks for your comments. I read that post in the past and it makes sense, although I tried `ui:repeat` too unsuccessfully as I said in the question. So how can I update the page section of a group of elements printed by a loop? – user1156544 Jun 07 '18 at 13:39

1 Answers1

0

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

user1156544
  • 1,725
  • 2
  • 25
  • 51
  • I use a ui:repeat for similar things (iirc), not sure why it does not work for you. Will try and see what makes this case actually not work... In the 'well-known-thing' post, you see that the poster uses `c:for-each` and that it is suggested to use a `ui:repeat` as an outer loop... So I'll check tonight... – Kukeltje Jun 07 '18 at 15:50
  • To be honest, I am not sure if the problem was where the `form` was located, and which id was used for the `render`. I also need to create a structure like: `` and I cannot use a `datatable` for that. So I have tried a list and `ui:repeat` and it seems to work in that place - so I am confused. I will do some more testing – user1156544 Jun 07 '18 at 16:15