带有 richfaces 4.3 的 JSF 2 - AJAX 请求和渲染时重复 id
JSF 2 with richfaces 4.3 - Duplicate id when AJAX request and rendering
有一个数据表,其中每一列header都有排序和过滤图标,分别对数据进行排序和过滤。
两者都工作正常。这里,"columnHeader" 是一个不同的文件,其中实现了 Composite。
如果我点击 Filter 图标,相应的 rich:popupPanel 弹出(包括关键字的 inputText 和操作的按钮),如果我点击另一个,
前一个被隐藏(显示:none),另一个被弹出。 "rich:popupPanel" 保持未关闭状态,除非手动关闭或单击另一个过滤器图标。
问题:
单击过滤器图标后,弹出 rich:popupPanel。如果我让它保持打开状态并执行排序,一切正常,但是,另一个重复的 popupPanel 已经在 DOM 中创建。两者的id相同。每次我排序时,每次都有一个新的 popupPanel 一次又一次地重复。因此,为相应的过滤器图标呈现了不止一个 popupPanel。我认为这是由于 AJAX 请求或渲染问题?
例如:
<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>
豆码:
...
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";
}
}
}
这是一个错误,当弹出窗口重新呈现时它不会自行销毁(因为在 DOM 中它通常位于其父项之外)。要绕过它,请在发出请求之前关闭弹出窗口或将其放在 table 之外,但我认为这不是一个选项。
顺便说一句,为什么不在数据 table 中使用自定义 sorting/filtering?其次,为什么不使用 JS API 隐藏弹出窗口而不是手动设置显示样式?
有一个数据表,其中每一列header都有排序和过滤图标,分别对数据进行排序和过滤。 两者都工作正常。这里,"columnHeader" 是一个不同的文件,其中实现了 Composite。 如果我点击 Filter 图标,相应的 rich:popupPanel 弹出(包括关键字的 inputText 和操作的按钮),如果我点击另一个, 前一个被隐藏(显示:none),另一个被弹出。 "rich:popupPanel" 保持未关闭状态,除非手动关闭或单击另一个过滤器图标。
问题: 单击过滤器图标后,弹出 rich:popupPanel。如果我让它保持打开状态并执行排序,一切正常,但是,另一个重复的 popupPanel 已经在 DOM 中创建。两者的id相同。每次我排序时,每次都有一个新的 popupPanel 一次又一次地重复。因此,为相应的过滤器图标呈现了不止一个 popupPanel。我认为这是由于 AJAX 请求或渲染问题?
例如:
<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>
豆码:
...
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";
}
}
}
这是一个错误,当弹出窗口重新呈现时它不会自行销毁(因为在 DOM 中它通常位于其父项之外)。要绕过它,请在发出请求之前关闭弹出窗口或将其放在 table 之外,但我认为这不是一个选项。
顺便说一句,为什么不在数据 table 中使用自定义 sorting/filtering?其次,为什么不使用 JS API 隐藏弹出窗口而不是手动设置显示样式?