问题
I'm using a omnifaces o:tree of "branches" where each branch has a "list of leafs" with the attribute "color" which should be editable in the tree.
- branch 0
- leaf 0 (color = "green")
- leaf 1 (color = "yellow")
- branch 1
- leaf 0 (color = "purple")
- branch 1_0
- leaf 1 (color = "red")
- leaf 2 (color = "orange")
- leaf 3 (color = "brown")
Adding/removing branches and adding leafs to any branch works fine and as expected. Also the rendering of any complex tree including all the list of leafes (displaying of the color attribute with the correct value) works like a charme.
But changing the color or removing a leaf (so whatever is within the ui:repeat
) only works as expected for the last rendered branch.
The "color" input fields for the other leafes/branches don't work at all and the delete leaf link within the ui:repeat
also doesn't work as for the leaf attribute in removeLeaf(...) always the corresponding leaf from the last rendered branch gets passed.
So in the above example clicking on delete for the yellow leaf from branch 0 will call removeLeaf(orange, branch 0) which will then oviously not delete anything as branch 0 has no orange leaf.
This is the code strapped down to the essential part - anything else can be provided if needed:
<h:form>
<o:tree value="#{treeBean.tree}" var="branchEntity" varNode="branchNode">
<o:treeNode>
<o:treeNodeItem>
<ui:repeat value="#{branchEntity.leafList}" var="leaf">
<h:panelGrid columns="2">
<p:inputText value="#{leaf.color}" />
<p:commandLink action="#{treeBean.removeLeaf(leaf, branchEntity)}" styleClass="ui-icon ui-icon-trash"
process="@form" update="@form" />
</h:panelGrid>
</ui:repeat>
<p:commandLink action="#{treeBean.addLeaf(branchEntity)}" styleClass="ui-icon ui-icon-plus"
process="@form" update="@form" />
<o:treeInsertChildren />
</o:treeNodeItem>
</o:treeNode>
</o:tree>
<p:commandButton id="save" value="Save" action="#{treeBean.save}" process="@form" update="@form" />
</h:form>
I know that there is some issue with nested ui:repeat
but as I'm on Mojarra 2.1.19 I guess it's not the case here. And actually nesting two ui:repeats
works fine if I replace o:tree
by a ui:repeat
which iterates over a list of branches. But then I obviously lost the tree functionality which I need. This I just tested to verify nested ui:repeat
s work properly.
This problem seems to be similar to another question but it's not exactly the same...
If I replace the the inner ui:repeat
by a p:dataList
(as suggested in some other answers to solve ui:repeat
problems and also now in BalusC answer here) the removeLeaf() links will all work proper but still only color input fields of the last branch will be bound.
UPDATE: I now had a closer look when using the p:dataList
. The POST content from the AJAX request which I got from my browsers developer tools looks good:
javax.faces.partial.ajax=true
javax.faces.source=hForm:save
javax.faces.partial.execute=hForm
javax.faces.partial.render=hForm
hForm:save=hForm:save
hForm=hForm
hForm:oTree:0:pDataList:0:color=green
hForm:oTree:0:pDataList:1:color=yellow
hForm:oTree:1:pDataList:0:color=purple
hForm:oTree:1_0:pDataList:0:color=red
hForm:oTree:1_0:pDataList:1:color=orange
hForm:oTree:1_0:pDataList:2:color=brown
javax.faces.ViewState=-6137230173999059936:-6718691551411872927
The treeBean.save() method is just logging the whole Tree.toString() to console which ends up like this:
[Branch [leafList=[Leaf [color=null], Leaf [color=null]]],
Branch [leafList=[Leaf [color=null]]]
[Branch [leafList=[Leaf [color=red], Leaf [color=orange], Leaf [color=brown]]]]]
If the color had some value != null before, the value remains the same - so it doesn't get overriden by null. I'm not yet deep enough into JSF to really know how to investigate where the information gets lost on it's way.
(I initially used Mojarra 2.1.19 on JBoss EAP 6.2 + Primefaces 5.2 + Omnifaces 2.1 but also experience exactly the same on Mojarra 2.2.12, TomEE, Primefaces 5.3 + Omnifaces 2.2. The complete Maven Eclipse sample project can be found here I added even one more simple String attribute (no List) directly to the Branch which works fine.)
回答1:
This is indeed caused by a state management bug in <ui:repeat>
. It didn't properly recognize itself being nested in another iterator and stubbornly clears out the state before being revisited during each iteration of the parent iterator. It only recognizes a parent UIRepeat
or UIData
, but that's it. The <o:tree>
doesn't and can't extend from UIData
, and the UIRepeat
is unfortunately a implementation-specific class and thus can't be used publicly as long as you want to be independent from a specific JSF implementation (Mojarra, MyFaces, etc).
Your best bet is to replace <ui:repeat>
by a real UIData
component which has a much better state management implementation. I just tried it here and adding items works just fine for me with <h:dataTable>
and <p:dataList>
. Only removing items failed with <h:dataTable>
, because it rendered wrong client IDs. It's not taking into account the container client ID. The <p:dataList>
worked for me in both cases of adding and removing items. Still updating the server side model of any bound input fields in the list fails as discussed in detail in the comments of this answer.
Replacing the entire <ui:repeat>
by a <p:dataList type="none">
(without type
, it will render an <ul><li>
) is the closest you can get.
<p:dataList type="none" value="#{branchEntity.leafList}" var="leaf">
...
</p:dataList>
World would have been much simpler if JSF offered a public UIRepeat
-like (marker) interface which all those repeaters could just check for when they're about to manage the state.
来源:https://stackoverflow.com/questions/34159489/uirepeat-in-otree-not-working-as-expected