当我在复合表中定义 2 个 actionListener 时,第一个 actionListener 将在我单击它时被调用两次,但第二个监听器工作正常而没有问题。即使我切换了 2 个听众,第一个总是叫了两次,第二个很好。
这是xhtml:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:asias="http://java.sun.com/jsf/composite/components/asias"
xmlns:test="http://java.sun.com/jsf/composite/test">
<h:head />
<h:body>
<h:form id="communityMembersForm">
<test:outertable list="#{memberController.members}"
title="Nested Table Test">
<f:actionListener for="sortAction"
binding="#{memberController.sortActionListener}" />
<f:actionListener for="actions"
binding="#{memberController.actionsActionListener}" />
</test:outertable>
</h:form>
</h:body>
</html>
MemberController 是一个 viewScoped 托管 bean。外部表定义为:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
xmlns:composite="http://java.sun.com/jsf/composite"
xmlns:test="http://java.sun.com/jsf/composite/test"
xmlns:asias="http://java.sun.com/jsf/composite/components/asias">
<composite:interface>
<composite:attribute name="list" required="true" />
<composite:attribute name="title" required="true" />
<composite:actionSource name="sortAction" targets="communityMembersPage" />
<composite:actionSource name="actions" targets="communityMembersPage" />
</composite:interface>
<composite:implementation>
<div>
<h3>#{cc.attrs.title}</h3>
<asias:memberTable id="communityMembersPage" memberList="#{cc.attrs.list}"
showPhone="true" showRoles="true" showAffiliation="true"
showLastLoginDate="true" showActions="true" allowSortLastName="true"
allowSortFirstName="true" allowSortEmail="true" />
</div>
</composite:implementation>
</html>
memberTable 定义为:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface name="memberTable"
displayName="A table for members of a community">
<composite:attribute name="memberList" required="true" />
<composite:attribute name="showPhone" type="java.lang.Boolean" default="false" />
<composite:attribute name="showRoles" type="java.lang.Boolean" default="false" />
<composite:attribute name="showAffiliation" type="java.lang.Boolean"
default="false" />
<composite:attribute name="showLastLoginDate" type="java.lang.Boolean"
default="false" />
<composite:attribute name="showActions" type="java.lang.Boolean"
default="false" />
<composite:attribute name="allowSortLastName" type="java.lang.Boolean"
default="false" />
<composite:attribute name="allowSortFirstName" type="java.lang.Boolean"
default="false" />
<composite:attribute name="allowSortEmail" type="java.lang.Boolean"
default="false" />
<composite:actionSource name="sortAction"
targets="dataTable:sortLastName dataTable:sortFirstName dataTable:sortEmail" />
<composite:actionSource name="actions" targets="dataTable:actions" />
</composite:interface>
<composite:implementation>
<div id="#{cc.clientId}" class="member-table">
<h:dataTable id="dataTable" value="#{cc.attrs.memberList}" var="member"
rowClasses="odd,even"
columnClasses="column1,column2,column3,column4,column5,column6,column7">
<h:column>
<f:facet name="header">
<h:commandLink id="sortLastName"
rendered="#{cc.attrs.allowSortLastName}">
<f:attribute name="sortBy" value="lastName" />
<span>#{i18n['last-name']}</span>
</h:commandLink>
<h:outputText rendered="#{not cc.attrs.allowSortLastName}"
value="#{i18n['last-name']}" />
</f:facet>
<span class='#{member.invalid ? "warning" : "" }'>#{member.lastName}</span>
</h:column>
<h:column>
<f:facet name="header">
<h:commandLink id="sortFirstName"
rendered="#{cc.attrs.allowSortFirstName}">
<f:attribute name="sortBy" value="firstName" />
<span>#{i18n['first-name']}</span>
</h:commandLink>
<h:outputText rendered="#{not cc.attrs.allowSortFirstName}"
value="#{i18n['first-name']}" />
</f:facet>
<span class='#{member.invalid ? "warning" : "" }'>#{member.firstName}</span>
</h:column>
<h:column>
<f:facet name="header">
<h:commandLink id="sortEmail"
rendered="#{cc.attrs.allowSortEmail}">
<f:attribute name="sortBy" value="email" />
<span>#{i18n['email-address']}</span>
</h:commandLink>
<h:outputText rendered="#{not cc.attrs.allowSortEmail}"
value="#{i18n['email-address']}" />
</f:facet>
<span class='#{member.invalid ? "warning" : "" }'>#{member.email}</span>
</h:column>
<h:column
rendered="#{cc.attrs.showPhone and loginUser.allowedToViewPhone}">
<f:facet name="header">#{i18n['phone-number']}</f:facet>
<span class='#{member.invalid ? "warning" : "" }'>#{member.phoneNumber}</span>
</h:column>
<h:column
rendered="#{cc.attrs.showRoles and loginUser.allowedToViewRoles}">
<f:facet name="header">#{i18n['roles']}</f:facet>
<span class='#{member.invalid ? "warning" : "" }'> <ui:repeat
value="#{member.roles}" var="role" varStatus="roleStatus">
<h:outputText rendered="#{roleStatus.index > 0}" value=", " />
#{role}
</ui:repeat>
</span>
</h:column>
<h:column
rendered="#{cc.attrs.showAffiliation and loginUser.allowedToViewAffiliation}">
<f:facet name="header">#{i18n['user-affiliation']}</f:facet>
<span class='#{member.invalid ? "warning" : "" }'>#{member.userAffiliation}</span>
</h:column>
<h:column
rendered="#{cc.attrs.showLastLoginDate and loginUser.allowedToViewLastLoginDate}">
<f:facet name="header">#{i18n['last-login-date']}</f:facet>
<h:outputText styleClass='#{member.invalid ? "warning" : "" }'
value="#{member.lastLoginDate}" converter="#{dateToWeeksOldConverter}" />
</h:column>
<h:column
rendered="#{cc.attrs.showActions and loginUser.allowedToViewActions}">
<f:facet name="header">#{i18n['actions']}</f:facet>
<h:commandLink id="actions">
<span>#{i18n['actions']}</span>
<f:attribute name="communityMember" value="#{member}" />
</h:commandLink>
</h:column>
</h:dataTable>
<h:outputText styleClass="warning" rendered="#{empty cc.attrs.memberList}"
value="#{i18n['no-records-found']}." />
</div>
</composite:implementation>
</html>
这是输出:
Dec 6, 2012 11:11:08 AM org.apache.catalina.startup.Catalina start
INFO: Server startup in 3328 ms
11:23:01,616 INFO MemberController:43 - constructing MemberController
11:23:07,446 DEBUG MemberController:139 - getSortActionListener called
11:23:07,456 DEBUG MemberController:180 - constructing comparator map
11:23:07,466 DEBUG MemberController:213 - constructing SortActionListener
11:23:07,476 DEBUG MemberController:227 - Sorting by lastName Ascending (MemberController$SortActionListener@779d9c0d)
11:23:07,476 DEBUG MemberController:139 - getSortActionListener called
11:23:07,476 DEBUG MemberController:227 - Sorting by lastName Descending (MemberController$SortActionListener@779d9c0d)
11:23:12,116 DEBUG MemberController:127 - getActionsActionListener called
11:23:12,126 DEBUG MemberController:132 - action for homer@simpsons.tv
从输出来看,第一个 actionListener 调用了两次,但第二个按预期只调用了一次。
这是使用 jsf 2.1.13 在 tomecat 6.0.36 下运行的。如果我使用 myfaces 而不是 jsf,就没有这个问题。