A question on the JDeveloper & ADF OTN forum about removing rows from a table which is based on a list of POJOs provides the reason for this blog post. The implementation, as simple as it is, holds a surprise.
The sample application build for this sample shows the POJO and the list of POJOs built from it. The list is lazy initialized at the time it’s first accessed (see https://tompeez.wordpress.com/2014/10/18/lazy-initalizing-beans/ for more info on this technique). On the only page a table build from this list.
<af:table value="#{viewScope.TableHandlerBean.phoneInfoList}" var="row" rowBandingInterval="0" id="t1" columnStretching="multiple" rowSelection="single" varStatus="rowStatus" styleClass="AFStretchWidth"> ...
In the last column we add a button which should remove the row.
... <af:column sortable="false" headerText="Remove" id="c4"> <af:commandButton text="Remove" id="cb1" actionListener="#{viewScope.TableHandlerBean.onRemoveAction}"> <af:setPropertyListener from="#{rowStatus.index}" to="#{viewScope.TableHandlerBean.currentSelectedIndex}" type="action"/> </af:commandButton> </af:column> ...
As the table is build on a list, we can’t use the default selection listener to get the selected row. Instead we use a setPropertyListener to pass the selected row index to a viewScope variable.
public void onRemoveAction(ActionEvent actionEvent) { Integer currentIndex = getCurrentSelectedIndex(); logger.info("Removing with index : >> " + currentIndex); logger.info("Removing with size : >> " + phoneInfoList.size()); logger.info("Value in List ** "+phoneInfoList.get(currentIndex).getSequence()+" phone "+phoneInfoList.get(currentIndex).getPhoneType()); phoneInfoList.remove(currentIndex); logger.info("size after removing : >> " + phoneInfoList.size()); UIComponent ui = (UIComponent) actionEvent.getSource(); AdfFacesContext.getCurrentInstance().addPartialTarget(ui.getParent().getParent()); } public Integer getCurrentSelectedIndex() { return currentSelectedIndex; }
The actionListener we use for the remove button picks up the row index and users the default remove() method of the list interface to remove the row from the list.
Finally we send a ppr to the table to visualize the change.
The surprise comes when we run the code and find that removing the selected row doesn’t work.
We don’t get an error message in the log. This is surprising as a simple delete of an element from an ArrayList should not be a problem.
Question is what happened and why?
Do you spot the problem in the code above?
A look at the Javadoc API for the List interface
shows two
remove
methods. We passed the index of the row, so it should have worked!But wait, a close look at the JavaDoc shows that one of the
remove
methods gets an object as parameter. Well, the return parameter of the getCurrentSelectedIndex()
method is an Integer
object!Now it’s clear. As we passed an object to the
remove
method the list implementation looks for an object which equals the passed object. Not finding one it doesn’t remove anything from the list and it don’t give an error message as this can be a normal result of this operation.The only hint we could have gotten is that the return parameter of the
remove
method will return null
instead of the removed element. This we did not check.Anyway, changing the code to
/** * Listener for the remove button in a table row * @param actionEvent tigger of the event */ public void onRemoveAction(ActionEvent actionEvent) { // Get the index to remove Integer currentIndex = getCurrentSelectedIndex(); logger.info("Removing with index : >> " + currentIndex); logger.info("Removing with size : >> " + phoneInfoList.size()); logger.info("Value in List ** " + phoneInfoList.get(currentIndex).getSequence() + " phone " + phoneInfoList.get(currentIndex).getPhoneType()); // remove the index. Here we need t opass the int value as the Integer would be interpreted as object to delete. As this object can't be found // the list would stay as is. There wouldn't even an error message. // To find out if the object was removed you hav eto check the return value. If it's not null it is the element which has been removed. PhoneInfoDTO dTO = phoneInfoList.remove(currentIndex.intValue()); logger.info("size after removing : >> " + phoneInfoList.size()); if (dTO == null) { logger.warning("Element with index " + currentIndex + " not found in list!"); } // get the source and trigger a ppr on the container the table resides in UIComponent ui = (UIComponent)actionEvent.getSource(); AdfFacesContext.getCurrentInstance().addPartialTarget(ui.getParent().getParent().getParent()); }
makes the application run as desired. The trick it to pass the int value of the currentIndex
Interger object.
You can download the sample (the final working one) from github BlogPoJoTableDeleteRow.zip. The sample runs without a DB connection.