p:dataTable how prevent f:viewParam being invoked on 1st rowEdit then tick save using JSF at XHTML level (not in bean)
Posted: 14 Apr 2017, 11:18
[EDIT 2017-04-24: now also as PrimeFaces community issue report. https://github.com/primefaces/primefaces/issues/2299 and with easy NetBeans 8.2 test app]
Hi PrimeFaces forum,
I have a Java backing-bean based workaround (explained below) for the problem the following behaviour causes, but I am not very happy with that workaround, and I would like to understand the behavior of p:dataTable row editing better and if possible I would like to find a purely XHTML-level solution to my problem. (BTW I am otherwise very experienced with p:dataTable, which I have used for some very complex applications for some years.)
It is not about an error or bug in p:dataTable, but the way it behaves during row editing causes me a problem in one situation.
The following code examples are completely simplified and adapted for this forum.
I have an entity Element, which has a relation List<Link> getLinks(), where Link is also an entity.
Element has an editor edit.xhtml.
The aim is to have an composite component links_editor.xhtml which can be embedded in the edit.xhtml.
There is a CDI-compliant @VIewScoped backing bean Manager.
The edit.xhtml relies on a viewParam to load an Element for editing:
<f:view>
<f:metadata>
<f:viewParam name="id" value="#{manager.id}"/>
</f:metadata>
</f:view>
Where in the backing bean:
In the links_editor.xhtml:
The row edit listener:
where JsfUtil above obviously just leverages FacesContext.getCurrentInstance().addMessage(...) for diagnostics.
The issue/concern:
If I edit a Link row in the p:dataTable a first time and then use the "tick save" the onLinkRowEdit listener is invoked, but the diagnostics show that the value appear not to have changed. The reason is that this f:viewParam is invoked:
This (via routes not shown in detail) loads the Element again from database via setId(Long id), so that in the composite component edit_links.xthml this is essentially reset #{cc.attrs.element.links}, any changes are discarded.
The interesting thing is that if (without reloading the entire @ViewScoped page) one edits the same p:dataTable row a second time that f:viewParam is NOT invoked, and thereafter it works as desired.
A workaround (rather hacky) is to "block" any attempt to reload the Element by id within the view scope:
To be clear, I am aware of the usual strategies for using @PostConstruct and/or lazy database fetching in getters for frequently accessed info under JSF in backing beans. And I don't want to abandon here the f:viewParam approach entirely (as it works well for other situations the same Manager bean is also used for).
My interest is specifically about Primefaces p:dataTable:
Q1: Why does p:dataTable need to call the f:viewParam during the 1st row edit then "tick save", when the row information (in this case element.links) is clearly already available ?
Q2: Why does p:dataTable NOT need to call the f:viewParam during the 2nd row edit then "tick save" ?
Q3: Is there an XHTML-base way of preventing p:dataTable from calling the f:viewParam at all during rowEdit and "tick save" ?
Hi PrimeFaces forum,
I have a Java backing-bean based workaround (explained below) for the problem the following behaviour causes, but I am not very happy with that workaround, and I would like to understand the behavior of p:dataTable row editing better and if possible I would like to find a purely XHTML-level solution to my problem. (BTW I am otherwise very experienced with p:dataTable, which I have used for some very complex applications for some years.)
It is not about an error or bug in p:dataTable, but the way it behaves during row editing causes me a problem in one situation.
The following code examples are completely simplified and adapted for this forum.
I have an entity Element, which has a relation List<Link> getLinks(), where Link is also an entity.
Element has an editor edit.xhtml.
The aim is to have an composite component links_editor.xhtml which can be embedded in the edit.xhtml.
There is a CDI-compliant @VIewScoped backing bean Manager.
The edit.xhtml relies on a viewParam to load an Element for editing:
<f:view>
<f:metadata>
<f:viewParam name="id" value="#{manager.id}"/>
</f:metadata>
</f:view>
Where in the backing bean:
Code: Select all
public void setId(Long id) {
if (id != null) {
//load an Element from database for editing by id
Code: Select all
<cc:implementation>
<div id="#{cc.clientId}">
<p:growl id="testgrowl"/>
<p:dataTable
id="links_table"
editable="true"
var="link"
value="#{cc.attrs.element.links}"
>
<p:ajax
event="rowEdit"
listener="#{cc.attrs.manager.onLinkRowEdit}"
update="#{cc.clientId}:testgrowl"
/>
<p:column headerText="Link title">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{link.name}" />
</f:facet>
<f:facet name="input">
<p:inputText id="name" value="#{link.name}"/>
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Link URL">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{link.urlAsString}" />
</f:facet>
<f:facet name="input">
<p:inputText id="url" value="#{link.urlAsString}""/>
<p:message
for="url"
display="icon"
/>
</f:facet>
</p:cellEditor>
</p:column>
<p:column>
<p:rowEditor />
</p:column>
</p:dataTable>
Code: Select all
public void onLinkRowEdit(RowEditEvent event) {
Link link = (Link) event.getObject();
try {
checkLink(link); //throws if URL string malformed
JsfUtil.addInfoMessage("DEBUG: Link: "+link);
} catch (LocalUpdateException ex) {
JsfUtil.addErrorMessage(ex.getMessage());
}
}
The issue/concern:
If I edit a Link row in the p:dataTable a first time and then use the "tick save" the onLinkRowEdit listener is invoked, but the diagnostics show that the value appear not to have changed. The reason is that this f:viewParam is invoked:
Code: Select all
<f:viewParam name="id" value="#{manager.id}"/>
The interesting thing is that if (without reloading the entire @ViewScoped page) one edits the same p:dataTable row a second time that f:viewParam is NOT invoked, and thereafter it works as desired.
A workaround (rather hacky) is to "block" any attempt to reload the Element by id within the view scope:
Code: Select all
public void setId(Long id) {
//HACK/WORKAROUND: prevent reload of Element by id
if (Objects.equals(id, this.id)) {
return;
}
if (id != null) {
//load an Element from database for editing by id
My interest is specifically about Primefaces p:dataTable:
Q1: Why does p:dataTable need to call the f:viewParam during the 1st row edit then "tick save", when the row information (in this case element.links) is clearly already available ?
Q2: Why does p:dataTable NOT need to call the f:viewParam during the 2nd row edit then "tick save" ?
Q3: Is there an XHTML-base way of preventing p:dataTable from calling the f:viewParam at all during rowEdit and "tick save" ?