1

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.

Neo
  • 1,337
  • 4
  • 21
  • 50
  • Hints: run the application in development mode, check the logging, check network traffic... etc... 'Not working' is to vague... – Kukeltje Dec 01 '17 at 13:18
  • Hi, the comment of a year ago is still valid! Without that (and without the details of what your JSFUtil does) it is hard to help – Kukeltje Nov 07 '18 at 09:27
  • Hi, I have added the JSFUtil's method. In the server log there is nothing special. What else can I check? How to analyse network traffic? – Neo Nov 07 '18 at 09:45
  • _"How to analyse network traffic? "_ Use Google... (and browser developer tools) – Kukeltje Nov 07 '18 at 10:12
  • I did, but what I have to look for? I don't see nothing special.... – Neo Nov 07 '18 at 10:14
  • Http requests in general. (and did you run in development mode). Please be more proactive. – Kukeltje Nov 07 '18 at 10:15
  • I would like to be more proactive, but I don't know what to check...sorry... I don't want to waste your time :-) Http request seems ok. Application is in Dev mode.This is the button's html: – Neo Nov 07 '18 at 11:59
  • But what happens when you press this button? A request? And what does the response contain? Exceptions in the server logging? [mvce] please and please mention jsf impl. and version. – Kukeltje Nov 07 '18 at 12:22
  • Yes, a request. Response contains the whole html of the page (as expected), just the bean's method is not called. JSF 2.2, myfaces-2.2.7 – Neo Nov 07 '18 at 12:50

1 Answers1

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;
  }
Neo
  • 1,337
  • 4
  • 21
  • 50