There is a datatable in which each column header has Sort and Filter icons, which do sorting and filtering of data respectively. Both are working fine. Here, "columnHeader" is a different file in which Composite is implemented. If I click in Filter icon, corresponding rich:popupPanel pops up (consists inputText for keyword and buttons for actions), and if I click another, the previous one gets hidden(display:none) and other gets poped up. "rich:popupPanel" remains unclosed until and unless its manually closed or if another filter icon is clicked.
Problem: After clicking the filter icon, the rich:popupPanel pops up. If I leave it open and perform Sort, everthing works fine but, another duplicate popupPanel is already created in the DOM. The ids of both are same. And every time I sort, each time a new popupPanel duplicates again and again. So more than one popupPanel is rendered for corresponding filter icons. I think it is due to AJAX request or the rendering issue?
for eg:
<html ...
xmlns:cpd="http://java.sun.com/jsf/composite/cpd">
<rich:dataTable value="#{bean.tableData}" var="data" id="myTable">
<rich:column>
<f:facet name="header">
<cpd:columnHeader backingbean="${bean}" columnid="empid" />
</f:facet>
<h:ouputText value="#{data.eid}" />
</rich:column>
<rich:column>
<f:facet name="header">
<cpd:columnHeader backingbean="${bean}" columnid="empname" />
</f:facet>
<h:ouputText value="#{data.ename}" />
</rich:column>
...
</rich:dataTable>
columnHeader.xhtml
<html ...>
<composite:interface>
<composite:attribute name="backingbean" />
<composite:attribute name="columnid" />
</composite:interface>
<composite:implementation>
<c:set var="backingbean" value="#{cc.attrs.backingbean}" />
<c:set var="columnid" value="#{cc.attrs.columnid}" />
<c:set var="columnFilterLink" value="#{columnid}_filterLink" />
<c:set var="columnFilterPopupId" value="#{cc.attrs.columnid}_columnFilterPopup" />
<table id="columnHeader">
<tr>
<td>
<h:outputLabel value="#{backingbean.columns[columnid].label}" escape="false" />
</td>
<td>
<!-- Sort -->
<a4j:commandLink id="column_#{columnid}_sortCommandLink" actionListener="#{backingbean.doSort}"
rendered="${backingbean.columns[columnid].sortable}" render="myTable">
<i class="fa fa-sort"/>
<a4j:param name="orderField" value="#{backingbean.columns[columnid].name}" />
<a4j:param name="order" value="#{backingbean.columns[columnid].nextSortOrder}" />
</a4j:commandLink>
<!-- Filter -->
<h:outputLink value="#" id="#{columnFilterLink}" rendered="#{empty backingbean.columns[columnid].filter}" onclick="closeAllOtherFilters('#{rich:clientId(columnFilterPopupId)}')" disabled="#{(backingbean.recordCount le 0)">
<rich:componentControl event="click" operation="show" target="#{columnFilterPopupId}">
<a4j:param name="event" value="event" noEscape="true" />
<rich:hashParam>
<a4j:param noEscape="true" name="top"
value="jQuery(#{rich:element(columnFilterLink)}.parentNode).offset().top + jQuery(#{rich:element(columnFilterLink)}.parentNode).height()" />
<a4j:param noEscape="true" name="left" value="jQuery(#{rich:element(columnFilterLink)}.parentNode).offset().left" />
</rich:hashParam>
</rich:componentControl>
<i class="fa fa-filter"/>
</h:outputLink>
<!-- Popup panel -->
<rich:popupPanel id="#{columnFilterPopupId}" >
<table cellspacing="0">
<tr><td>
<h:outputLabel value="#{backingbean.columns[columnid].label}" escape="false" />
</td>
<td align="right">
<h:outputLink value="#" onclick="closeFilters('#{rich:clientId(columnFilterPopupId)}');#{rich:component(columnFilterPopupId)}.hide(); return false;">
X </h:outputLink>
</td>
</tr>
<tr><td colspan="2"><div class="d2-separator" /></td></tr>
<tr><td colspan="2">
<h:inputText styleClass="columnFilterInputText" id="#{columnFilterInputText}" style="width:98%" value="#{backingbean.columns[columnid].filter}"/>
<!-- <rich:inplaceInput id="#{columnFilterInputText}" defaultLabel="enter filter string" style="width:159px" value="#{backingbean.columns[columnid].filter}"/> -->
</td>
</tr>
<tr><td colspan="2">
<a4j:commandButton id="#{columnid}_filterButton" actionListener="#{backingbean.doFilter}"
value="Apply Filter" onclick="if(validateFilter('#{rich:clientId(columnFilterInputText)}','#{backingbean.columns[columnid].filterType}')){#{rich:component(columnFilterPopupId)}.hide(event); return true;} else return false;"
render="myTable">
</a4j:commandButton>
<a4j:commandButton id="#{columnid}_resetButton" actionListener="#{backingbean.doFilter}"
value="Reset Filter" onclick="resetFilterForTable('#{rich:clientId(columnFilterInputText)}');#{rich:component(columnFilterPopupId)}.hide(event); return true;"
render="myTable">
</a4j:commandButton>
</td></tr><tr><td colspan="2"></td></tr>
</table>
</rich:popupPanel>
</composite:implementation>
</html>
Bean code:
...
public void doSort(ActionEvent evt) {
super.doSort(evt);
loadTable();
}
...
BaseController : (Bean extends BaseController)
public void doSort(ActionEvent evt) {
dataSetParameters.setPageNo(1);
String orderField = (String) resolveFromRequestParameterMap("orderField");
if (!orderField.equalsIgnoreCase(getOrderField())
&& getOrderField() != null
&& getColumns().get(getOrderField()) != null)
getColumns().get(getOrderField()).setSortOrder("");
setOrderField((String) resolveFromRequestParameterMap("orderField"));
setOrder((String) resolveFromRequestParameterMap("order"));
getColumns().get(getOrderField()).setSortOrder(getOrder());
}
Javascript:
function closeAllOtherFilters(filterid){
var nodes = document.querySelectorAll('.columnFilterPopup')
for (var i=0; i<nodes.length; i++){
var outerNodeId = nodes[i].id.replace(/_container/i, '');
var outerNodeElement = document.getElementById(outerNodeId);
if(outerNodeId != filterid){
if (outerNodeElement.style.display=="block"){
outerNodeElement.style.display="none";
}
if (nodes[i].style.display=="block"){
nodes[i].style.display="none";
}
}else{
nodes[i].style.display="block";
outerNodeElement.style.display="block";
}
}
}
It's a bug, when the popup is getting rerendered it doesn't destroy itself (because it's usually placed outside of its parent in DOM). To go around it close the popup before you fire the request or place it outside of the table but I assume that's not an option.
Btw why not use a custom sorting/filtering in the datatable? And secondly why not hide the popups with the JS API instead of manually setting the display styles?