Reputation: 14003
We have a requirement to allow users to configure the order of columns in all datatables, including the columns that have action buttons on them, here p:commandButton
s.
Hence we are using binding for all our columns which we must instantiate manually. For all columns that only display some strings, booleans, dates, and numbers all works fine, however problems start when adding p:commandButton
s to the columns that have one or more <f:setPropertyActionListener>
s on them.
ELContext elContext = FacesContext.getCurrentInstance().getELContext();
ExpressionFactory factory = FacesContext.getCurrentInstance().getApplication().getExpressionFactory();
CommandButton button = new CommandButton();
button.setIcon( "ui-icon ui-icon-pencil" );
button.setProcess( "@this" );
button.setUpdate( ":compliance-case-dialog :compliance-case-form:data" );
button.setOncomplete( "complianceCaseDialog.show();" );
ValueExpression targetExpression = factory.createValueExpression( elContext, "#{complianceCaseManager.selectedComplianceCaseWithRefresh}", ComplianceCase.class );
ValueExpression valueExpression = factory.createValueExpression( elContext, "#{coca}", ComplianceCase.class );
button.addActionListener( new SetPropertyActionListenerImpl( targetExpression, valueExpression ) );
column.getChildren().add( button ); // add to Column instance
This button "correctly" displays a <p:dialog>
, but should call ComplianceCaseManager
class' setSelectedComplianceCaseWithRefresh( ComplianceCase selectedComplianceCase )
method with the datatable's current entity (as defined by var="coca"
) before showing the dialog.
<p:dataTable id="data"
widgetVar="resultTable"
value="#{complianceCaseManager.complianceCases}"
var="coca"
...>
</p:dataTable>
Debugging shows the setSelectedComplianceCaseWithRefresh( ComplianceCase selectedComplianceCase )
method is never called.
Q:
What's wrong? How do you fix this?
PS: config is PrimeFaces 3.4.2, Mojarra 2.1.22, GlassFish 3.1.2.2, Java 7
Update 1:
Here's the p:commandButton I want to translate to programmatic:
<p:commandButton icon="ui-icon ui-icon-pencil"
title="#{msg['complianceCaseManager.data.edit.button.hint']}"
process="@this"
update=":compliance-case-dialog :compliance-case-form:data"
oncomplete="complianceCaseDialog.show();">
<p:resetInput target=":unlocked-form" />
<f:setPropertyActionListener target="#{complianceCaseManager.selectedComplianceCaseWithRefresh}" value="#{coca}" />
<f:setPropertyActionListener target="#{complianceCaseManager.mode}" value="EDIT" />
</p:commandButton>
The line
<f:setPropertyActionListener target="#{complianceCaseManager.selectedComplianceCaseWithRefresh}" value="#{coca}" />
is the one I must get to work.
The solution by kolossus to create a MethodExpressionActionListener
did not work for my code:
String targetExpressionString = "#{complianceCaseManager.selectedComplianceCaseWithRefresh}";
String valueExpressionString = "#{coca}";
MethodExpression targetMethodExpression = factory.createMethodExpression( elContext, targetExpressionString, null, new Class<?>[]{ ComplianceCase.class } );
MethodExpression valueMethodExpression = factory.createMethodExpression( elContext, valueExpressionString, ComplianceCase.class, new Class<?>[0] );
button.addActionListener( new MethodExpressionActionListener( targetMethodExpression, valueMethodExpression ) );
AFAIK is made so that on the target the setter is called and on the value the setter is called, so I'd assume the ValueExpression
s to be sufficient.
Update 2:
Trying to set the current entity via EL 2.2 method call also doesn't work.
Code:
String methodExpressionString = "#" + "{complianceCaseManager.selectedComplianceCaseWithRefresh(coca)}";
MethodExpression methodExpression = factory.createMethodExpression( elContext, methodExpressionString, null, new Class<?>[]{ ComplianceCase.class } );
button.addActionListener( new MethodExpressionActionListener( methodExpression ) );
Nothing is called.
Update 3:
Here's the correct <:setPropertyActionListener>
code:
// traditional <f:setPropertyActionListener target="#{complianceCaseManager.selectedComplianceCaseWithRefresh}" value="#{coca}" />
ValueExpression targetExpression = factory.createValueExpression( elContext, "#{complianceCaseManager.selectedComplianceCaseWithRefresh}", ComplianceCase.class );
ValueExpression valueExpression = factory.createValueExpression( elContext, "#{coca}", ComplianceCase.class );
button.addActionListener( new SetPropertyActionListenerImpl( targetExpression, valueExpression ) );
Big thanks to kolossus. Remember to call setId()
when instantiating a PrimeFaces CommandButton
.
Upvotes: 3
Views: 5790
Reputation: 1
I used to solve it by using a lambda expression:
private final ActionListener actionListener = (ActionEvent event) -> {
//code to execute
};
and then just:
comandButton.setId(id);
commandButon.addActionListener(actionListener);
Upvotes: 0
Reputation: 20691
An actionListener should be created as a MethodExpression
not a ValueExpression
:
I'm assuming that factory instanceOf ExpressionFactory
. Use the createMethodExpression factory
MethodExpression targetExpression = factory.createMethodExpression( elContext, "#{complianceCaseManager.selectedComplianceCaseWithRefresh(coca)}",null,new Class[]{ComplianceCase.class} );
Adding the listener to the component:
button.addActionListener( new MethodExpressionActionListener(targetExpression));
Not entirely related to the current problem, you also omitted the id
attribute of the component. To be safe, add
button.setId("theButton");
EDIT: You absolutely have to set the id
attribute when dynamically creating command components
Upvotes: 1