问题
I'm trying to create an HtmlCommandButton programmatically, following the example here
http://javaevangelist.blogspot.ch/2013/01/jsf-21-tip-of-day-programmatically.html
Everything works fine (i.e., the actionListener is called) if I add the ajax behavior, it doesn't work if ajax is turned off.
Backing bean:
@Named
@RequestScoped
public class CommandBean implements Serializable {
public String generateUUID() {
return java.util.UUID.randomUUID().toString();
}
}
Solution 1 (with ajax)
private HtmlCommandButton createCommandButtonWithAjax(final FacesContext context,
final String methodExpression, final String value) {
Application application = context.getApplication();
Class<?>[] clazz = new Class<?>[]{};
HtmlCommandButton htmlCommandButton =
(HtmlCommandButton) application.createComponent(HtmlCommandButton.COMPONENT_TYPE);
htmlCommandButton.setValue(value);
AjaxBehavior ajaxBehavior = (AjaxBehavior) FacesContext.getCurrentInstance().getApplication().createBehavior(AjaxBehavior.BEHAVIOR_ID);
((UIComponentBase)htmlCommandButton).addClientBehavior("click", ajaxBehavior);
MethodExpression actionListener = application.getExpressionFactory().createMethodExpression(FacesContext.getCurrentInstance().getELContext(), action, String.class, clazz);
button.addActionListener(new MethodExpressionActionListener(actionListener));
return htmlCommandButton;
}
Solution 2 (no ajax)
private HtmlCommandButton createCommandButton(final FacesContext context,
final String methodExpression, final String value) {
Application application = context.getApplication();
Class<?>[] clazz = new Class<?>[]{};
HtmlCommandButton htmlCommandButton =
(HtmlCommandButton) application.createComponent(HtmlCommandButton.COMPONENT_TYPE);
htmlCommandButton.setValue(value);
htmlCommandButton.setActionExpression(JSFUtils.createMethodExpression(methodExpression, String.class, clazz));
return htmlCommandButton;
}
Calling code:
createCommandButton(FacesContext.getCurrentInstance(),
"#{commandBean.generateUUID()}", "Generate UUID");
JSFUtils:
public static MethodExpression createMethodExpression(String methodExpression,Class<?> expectedReturnType,Class<?>[] expectedParamTypes) {
FacesContext context = FacesContext.getCurrentInstance();
return context.getApplication().getExpressionFactory()
.createMethodExpression(context.getELContext(), methodExpression, expectedReturnType, expectedParamTypes);
}
Solution 1 is working, solution 2 not: the bean method generateUUID() is not called. I have tried also with htmlCommandButton.setImmediate(true)
to exclude validation errors.
回答1:
Apparently we need a Custom AjaxBehavior, as suggested here: https://forum.primefaces.org/viewtopic.php?f=3&t=5344 and here How to programmatically add an AjaxBehavior to a UIComponent with primefaces
Custom Ajax:
import java.util.HashMap;
import javax.el.ELContext;
import javax.el.MethodExpression;
import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.BehaviorEvent;
public class MyAjaxBehavior extends AjaxBehavior{
@Override
public Object saveState(FacesContext context) {
HashMap<String, Object> map;
map = new HashMap<String, Object>();
map.put( "update", getUpdate() );
map.put( "process", getProcess() );
map.put( "oncomplete", getOncomplete() );
map.put( "onerror", getOnerror() );
map.put( "onsuccess", getOnsuccess() );
map.put( "onstart", getOnstart() );
map.put( "listener", getListener() );
if (initialStateMarked()) return null;
return UIComponentBase.saveAttachedState(context, map);
}
@SuppressWarnings("unchecked")
@Override
public void restoreState(FacesContext context, Object state) {
if (state != null){
HashMap<String, Object> map;
map = (HashMap<String, Object>) UIComponentBase.restoreAttachedState(context, state);
setUpdate( (String) map.get( "update" ));
setProcess( (String) map.get( "process"));
setOncomplete( (String) map.get( "oncomplete" ));
setOnerror( (String) map.get( "onerror" ));
setOnsuccess( (String) map.get( "onsuccess" ));
setOnstart( (String) map.get( "onstart" ));
setListener( (MethodExpression) map.get( "listener" ));
}
}
@Override
public void broadcast(BehaviorEvent event) throws AbortProcessingException {
ELContext eLContext = FacesContext.getCurrentInstance().getELContext();
//Backward compatible implementation of listener invocation
if(getListener() != null) {
try {
getListener().invoke(eLContext, new Object[]{event});
} catch(IllegalArgumentException exception) {
getListener().invoke(eLContext, new Object[0]);
}
}
}
}
Create Button
private HtmlCommandButton createCommandButtonWithAjax(final FacesContext context,
final String methodExpression, final String value) {
Application application = context.getApplication();
Class<?>[] clazz = new Class<?>[]{};
HtmlCommandButton htmlCommandButton =
(HtmlCommandButton) application.createComponent(HtmlCommandButton.COMPONENT_TYPE);
htmlCommandButton.setValue(value);
addPrimefacesAjaxSupport(htmlCommandButton,"click", methodExpression);
return htmlCommandButton;
}
add AjaxBehavior
private AjaxBehavior addPrimefacesAjaxSupport(UIComponentBase comp, String event, String actionListener){
MyAjaxBehavior ajaxBehavior = new MyAjaxBehavior();
ajaxBehavior.setListener( JSFUtils.createMethodExpression(actionListener, void.class,new Class[]{ ActionEvent.class}) );
ajaxBehavior.setProcess( "@this" );
comp.addClientBehavior( event, ajaxBehavior );
return ajaxBehavior;
}
来源:https://stackoverflow.com/questions/47589580/jsf-htmlcommandbutton-programmatically-bean-method-not-called-if-ajax-turned-o