PrimeFaces 3.0 - f:ajax Ajax Group not receiving events from p:commandButton or p:commandLink
I am trying to integrate PrimeFaces 3.0 into my JSF 2.0 project. I've created some example code to try and get a PrimeFaces <p:dataTable>
with a delete button/link in a column for each row. I want the delete action to trigger an Ajax refresh of the table (my current code triggers a render of the enclosing form using @form).
My problem is that the PrimeFaces components do not appear to be firing events that can be handled by the core JSF <f:ajax>
tag. I am using <f:ajax>
as an Ajax Group that encloses the <p:dataTable>
as well as another command button.
If I implement this purely using core JSF elements (<h:dataTable>
, <h:commandButton>
, etc.) everything works as expected. Pardon the ugly output, but I'm not going to waste time on CSS styling for my example code.
Here is the code for the core JSF implementation page:
<h:form id="dataTableForm">
<f:ajax
listener="#{dataTableTestBean.processAjaxBehavior}"
event="action"
render="@form :messages :ajaxRenderTargetsInTemplate">
<h:panelGroup id="dataTablePanelGroup">
<h:commandLink
id="commandLinkAddRow"
value="Add Sample Row"
actionListener="#{dataTableTestBean.handleAddDataTableRow}" />
<h:commandButton
id="commandButtonAddRow"
value="Add Sample Row"
actionListener="#{dataTableTestBean.handleAddDataTableRow}" />
<sandbox:dataTableCC
id="dataTableCC"
valueList="#{dataTableTestBean.dataTableList}" />
</h:panelGroup>
</f:ajax>
<h:panelGroup rendered="#{empty dataTableTestBean.dataTableList}">
<h3>No Records To Display</h3>
</h:panelGroup>
<h:commandButton
id="commandButtonBackToStartPage"
value="#{bundle.backToStartPage}"
action="start"
immediate="true" />
</h:form>
Here is the code for the core JSF implementation CC:
<composite:implementation>
<h:dataTable
id="theDataTable"
rendered="#{not empty cc.attrs.valueList}"
value="#{cc.attrs.valueList}"
var="row"
emptyMessage="#{bundle.dataTableEmptyMessage}">
<f:facet name="header">#{bundle.dataTableHeader}</f:facet>
<h:column id="columnDeleteButton">
<h:commandLink
id="commandLinkDelete"
value="Delete"
action="#{dataTableTestBean.actionDeleteDataTableRow(row)}" />
</h:column>
<h:column>
<h:commandButton
alt="#{bundle.delete}"
image="#{resource['images/onebit_33_24x24.gif']}"
action="#{dataTableTestBean.actionDeleteDataTableRow(row)}" />
</h:column>
<h:column sortBy="#{row.name}">
<f:facet name="header">
#{bundle.columnHeaderName}
</f:facet>
<h:outputText value="#{row.name}" />
</h:column>
<h:column sortBy="#{row.rank}">
<f:facet name="header">
#{bundle.columnHeaderRank}
</f:facet>
<h:outputText value="#{row.rank}" />
<f:facet name="footer">
#{bundle.columnFooterRank}
</f:facet>
</h:column>
<h:column sortBy="#{row.serialNumber}">
<f:facet name="header">
#{bundle.columnHeaderSerialNumber}
</f:facet>
<h:outputText value="#{row.serialNumber}" />
<f:facet name="footer">
#{bundle.columnFooterSerialNumber}
</f:facet>
</h:column>
<f:facet name="footer">#{bundle.dataTableFooter}</f:facet>
</h:dataTable>
</composite:implementation>
The core JSF implementation will nicely fire the Ajax events and do the Partial Page Rendering, part of which is the render of an <h:messages>
that confirms the event was processed successfully.
When I replace the <h:dataTable>
with a <p:dataTable>
, no problem. However, if I replace the <h:commandButton>
and <h:commandLink>
with <p:commandButton>
and <p:commandLink>
, the Ajax events are not making it to the <f:ajax>
Ajax Group element. The record delete and insert operations do actually happen on the server, but the page is never refreshed. I will appear to the user that nothing happened. If you click the br开发者_运维技巧owser Refresh button, you will see that the operation did indeed happen. If you click the core JSF command button or command link, everything works. The PrimesFaces table renders and even the PrimeFaces "growl" popups are displayed as expected.
Here is a screenshot of the PrimeFaces implementation that has the problem. Again, pardon the ugliness of the buttons and links.
First, the page:
<p:growl id="growl" showSummary="false" showDetail="true" />
<h:form id="dataTableForm" styleClass="ui-widget">
<f:ajax
listener="#{dataTableTestBean.processAjaxBehavior}"
event="action"
render="@form :growl :ajaxRenderTargetsInTemplate">
<h:panelGroup id="dataTablePanelGroup">
<p:commandLink
id="pfCommandLinkAddRow"
value="Add Sample Row (pf)"
ajax="true"
actionListener="#{dataTableTestBean.handleAddDataTableRow}" />
<p:commandButton
id="pfCommandButtonAddRow"
value="Add Sample Row (pf)"
ajax="true"
actionListener="#{dataTableTestBean.handleAddDataTableRow}" />
<h:commandLink
id="hCommandLinkAddRow"
value="Add Sample Row (h)"
actionListener="#{dataTableTestBean.handleAddDataTableRow}" />
<h:commandButton
id="hCommandButtonAddRow"
value="Add Sample Row (h)"
actionListener="#{dataTableTestBean.handleAddDataTableRow}" />
<sandbox:primeFacesDataTableCC
id="dataTableCC"
valueList="#{dataTableTestBean.dataTableList}" />
</h:panelGroup>
</f:ajax>
<h:panelGroup rendered="#{empty dataTableTestBean.dataTableList}">
<h3>No Records To Display</h3>
</h:panelGroup>
<h:commandButton
id="commandButtonBackToStartPage"
value="#{bundle.backToStartPage}"
action="start"
immediate="true" />
</h:form>
and the CC:
<composite:implementation>
<p:dataTable
id="theDataTable"
rendered="#{not empty cc.attrs.valueList}"
value="#{cc.attrs.valueList}"
var="row"
emptyMessage="#{bundle.dataTableEmptyMessage}">
<f:facet name="header">#{bundle.dataTableHeader}</f:facet>
<p:column id="columnpclDelete">
<p:commandLink
id="pclDelete"
value="Delete (pf)"
action="#{dataTableTestBean.actionDeleteDataTableRow(row)}" />
</p:column>
<p:column id="columnpcbDeleteIconOnly">
<p:commandButton
id="pcbDeleteIconOnly"
ajax="true"
global="true"
alt="#{bundle.delete}"
image="ui-icon ui-icon-trash"
action="#{dataTableTestBean.actionDeleteDataTableRow(row)}" />
</p:column>
<p:column id="columnhclDelete">
<h:commandLink
id="hclDelete"
value="Delete (h)"
action="#{dataTableTestBean.actionDeleteDataTableRow(row)}" />
</p:column>
<p:column id="columnhcbDeleteWithImage">
<h:commandButton
id="hcbDeleteWithImage"
alt="#{bundle.delete}"
image="#{resource['images/onebit_33_24x24.gif']}"
action="#{dataTableTestBean.actionDeleteDataTableRow(row)}" />
</p:column>
<p:column id="columnName" sortBy="#{row.name}">
<f:facet name="header">
#{bundle.columnHeaderName}
</f:facet>
<h:outputText value="#{row.name}" />
</p:column>
<p:column id="columnRank" sortBy="#{row.rank}">
<f:facet name="header">
#{bundle.columnHeaderRank}
</f:facet>
<h:outputText value="#{row.rank}" />
<f:facet name="footer">
#{bundle.columnFooterRank}
</f:facet>
</p:column>
<p:column id="serialNumber" sortBy="#{row.serialNumber}">
<f:facet name="header">
#{bundle.columnHeaderSerialNumber}
</f:facet>
<h:outputText value="#{row.serialNumber}" />
<f:facet name="footer">
#{bundle.columnFooterSerialNumber}
</f:facet>
</p:column>
<f:facet name="footer">#{bundle.dataTableFooter}</f:facet>
</p:dataTable>
</composite:implementation>
Where have I gone wrong? Everything works correctly except for the Ajax events from the PrimeFaces components.
So apparently the PrimeFaces <p:commandButton>
and <p:commandLink>
tags cannot be used interchangeably with <h:commandButton>
and <h:commandLink>
. I thought they supported the base behavior of the core JSF tags and then provided more PrimeFaces-specific stuff in addition to that. This is not the case. The PrimeFaces tags are different and only provide some of the behavior of the core JSF tags of the same name.
So the solution to my problem is not really ideal. The PrimeFaces tags will not participate in an Ajax group using <f:ajax>
to enclose a group of controls (as you can do with the core JSF tags). Also, the PrimeFaces command button and command link will not use a <f:ajax>
or <p:ajax>
child. There are two attributes that it will look for to enable Ajax behavior: 'update' and 'ajax'.
Each tag must have an 'update' attribute that contains a space-separated list of Ajax render targets, just like the 'render' attribute of <f:ajax>
. My PrimeFaces components are inside a Composite Component, so I have had to pass the list of render targets via a CC interface attribute.
My command button inside the CC looks like this now:
<p:commandButton
id="pCommandButtonDeleteIconOnly"
alt="#{bundle.delete}"
image="ui-icon ui-icon-trash"
ajax="true"
update="#{cc.attrs.ajaxRenderTargets}"
action="#{dataTableTestBean.actionDeleteDataTableRow(row)}" />
If I didn't want the command button to execute as an Ajax call, then apparently the 'ajax' attribute should be set to "false" and the 'update' attribute should not be provided at all. This SO question helped point me in the right direction.
There are strange issues that can crop up if you are using an <f:ajax>
tag for Primefaces components.
Try utilizing the Primefaces ajax tag, <p:ajax>
EDIT: I just want to point out that you may run into an issue with an ajax refresh of a <p:dataTable>
from a component within that dataTable. This bug existed as of Primefaces 2.2.1 however it may be fixed in 3.0.
精彩评论