Search code examples
ajaxjsf-2commandbutton

JSF HtmlCommandButton programmatically - Bean method not called if ajax turned off


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.


Solution

  • 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;
      }