问题
Finally I've managed how to build a true dynamic table with the DOM-like approach in JSF when the data table structure is defined according to an external source. ( DataTable with dynamic columns exactly what I need) Basically, the following code is quite enough for me:
<p:layoutUnit position="west">
<!-- 2 -->
<p:tree value="#{tableViewsPageBean.root}" var="node" dynamic="true" cache="false"
selectionMode="single" selection="#{tableViewsPageBean.selectedNode}">
<!-- 3 -->
<p:ajax event="select" listener="#{tableViewsPageBean.onNodeSelect}"/>
<p:treeNode id="treeNode">
<h:outputText value="#{node}"/>
</p:treeNode>
</p:tree>
</p:layoutUnit>
<p:layoutUnit position="center">
<h:panelGroup>
<h:panelGroup rendered="#{tableViewsPageBean.isRegeneratable}">
<p:commandButton value="#{fu:$(languageBean, 'REGENERATE')}"
action="#{dynamicTableViewBean.regenerate}"/>
</h:panelGroup>
<!-- 1 -->
<h:panelGroup binding="#{dynamicTableViewBean.dataTableGroup}"/>
</h:panelGroup>
</p:layoutUnit>
But there is a problem I currently do not know how to solve. If you take a look at the code above, you can see the numeric markers in the comments that represent the flow of the backing beans methods:
binding="#{dynamicTableViewBean.dataTableGroup}"
selection="#{tableViewsPageBean.selectedNode}"
listener="#{tableViewsPageBean.onNodeSelect}"
This order is not good for me, because the binding of the h:panelGroup
must be defined after a user changed the selection in the tree view. Simply saying, the very best flow would be:
selection="#{tableViewsPageBean.selectedNode}"
(previously 2)listener="#{tableViewsPageBean.onNodeSelect}"
(previously 3)binding="#{dynamicTableViewBean.dataTableGroup}"
(previously 1)
so I could build a data table component respecting the user selection in the tree. So is it a way to force another order of executing the methods above? PrimeFaces version used: 3.2
Thanks in advance. Your help is appreciated a lot.
回答1:
It worked that way in old JSF 1.x on JSP, but not anymore in JSF 2.x on Facelets. But the invocation order should not matter. Just make the binding
getter/setter a normal getter/setter and manipulate the dataTableGroup
inside the listener method instead. It's after all the same object reference.
public void onNodeSelect(NodeSelectEvent event) {
dataTableGroup.getChildren().add(...);
// ...
}
You only need to add the update
attribute to <p:ajax>
in order to really update the bound panelgroup or one of its parents when the ajax request completes, otherwise you will effectively see no change in the view.
E.g.
<h:form id="formId">
....
<p:ajax event="select" listener="#{tableViewsPageBean.onNodeSelect}" update=":formId:groupId" />
...
<h:panelGroup id="groupId" binding="#{dynamicTableViewBean.dataTableGroup}"/>
...
</h:form>
By the way, are you aware of PrimeFaces' <p:columns> component? It may be a better solution if all you want is dynamic columns. That solution didn't exist in flavor of any UIComponent
back in the dark JSF 1.x ages, that's why the awkward binding
workaround was been applied.
回答2:
You cannot force binding expression to be executed afterwards, because it's a feature of JSF lifecycle - to set bindings at the very beginning of request processing (during phase "Restore View").
I would also recommend to trigger model-based tree updates from action listeners (i.e. in #{tableViewsPageBean.onNodeSelect}
and/or #{dynamicTableViewBean.regenerate}
), because they are executed on phase "Invoke Application" (after submitted values have been converted, validated, and applied to the model objects) just before "Render Response".
You can still use PanelGroup's reference obtained from binding expression and manipulate with its children in Java code.
来源:https://stackoverflow.com/questions/10384959/jsf-respecting-a-component-binding-method-after-ui-driven-events-are-processed