user1323246
user1323246

Reputation: 433

JSF custom panel with button - action not invoked

I have built a custom component button, but somehow the action is not invoked. When debugging the getAction-Method within the component and invoking the supplied MethodeExpression the Bean-Method is called as expected. But due to some reason, the Expression is not invoked when pressing the button in the browser.

Is there some kind of additional Interface necessary to pass the action to the embedded button-component?

Any help is very appreciated since I am stuck at this issue for some days now

MyClass:

public class MyClass extends UIPanel implements SystemEventListener
{

private UIForm              form;
private HtmlCommandButton   buttonOk;

public MyClass()
{
    FacesContext context = getFacesContext();
    UIViewRoot root = context.getViewRoot();
    root.subscribeToViewEvent(PostAddToViewEvent.class, this);
}


@Override
public void processEvent(SystemEvent event)
{
    this.form = new UIForm();
    this.buttonOk = new HtmlCommandButton();
    this.buttonOk.setId("okButtonId");
    this.buttonOk.setActionExpression(getAction());
    this.buttonOk.setValue("OK");
    this.form.getChildren().add(this.buttonOk);
    getChildren().add(this.form);
}


private enum PropertyKeys
{
    action, text, titel
}


public MethodExpression getAction()
{
    return (MethodExpression) getStateHelper().eval(PropertyKeys.action);
}


public void setAction(MethodExpression actionExpression)
{
    getStateHelper().put(PropertyKeys.action, actionExpression);
}


public String getText()
{
    return (String) getStateHelper().eval(PropertyKeys.text);
}


public void setText(String text)
{
    getStateHelper().put(PropertyKeys.text, text);
}


public String getTitel()
{
    return (String) getStateHelper().eval(PropertyKeys.titel);
}


public void setTitel(String titel)
{
    getStateHelper().put(PropertyKeys.titel, titel);
}


@Override
public void encodeAll(FacesContext context) throws IOException
{
    ResponseWriter writer = context.getResponseWriter();
    writer.startElement(HTML.DIV_ELEM, this);
    writer.writeText(getText(), null);
    this.form.encodeAll(context);
    writer.endElement(HTML.DIV_ELEM);
}


@Override
public void encodeChildren(FacesContext context) throws IOException
{

}


@Override
public boolean isListenerForSource(Object source)
{
    return (source instanceof MyClass);
}

}

MyClassHandler:

public class MyClassHandler extends ComponentHandler
{

public MyClassHandler(ComponentConfig config)
{
    super(config);
}


@SuppressWarnings("rawtypes")
@Override
protected MetaRuleset createMetaRuleset(Class type)
{
    return super.createMetaRuleset(type).addRule(new MethodRule("action", String.class, new Class[] { ActionEvent.class }));
}

}

myView Method:

...
public String myMethod()
{
    System.err.println("myMethod");
    return "/some/path/yadayada.xhtml";
}
...

MyView.xhtml

<myTag action="#{myView.myMethod}" id="id1" titel="bla" text="bleh" />

Upvotes: 2

Views: 700

Answers (1)

lostiniceland
lostiniceland

Reputation: 3789

Exdending UICommand is enough, since you only want one action to be executed.

You have to provide two additional MethodExpressions via the tag-attributes and within the decode-method you can check which button has been pressed and redirect the particular MethodExpression to the standard-action provided by UICommand. This way, you dont have to worry about the legacy-interface ActionSource, or how Events are broadcasted.

public void decode(FacesContext contex)
{
    Map<String,String> map = context.getExternalContext.getRequestParameterMap();
    // your rendered buttons need a name you check for
    final boolean okPressed = map.containsKey( getClientId + ":ok" ); 
    final boolean cancelPressed = map.containsKey( getClientId + ":cancel" );
    if(okPressed || cancelPressed)
    {
        MethodExpression exp = null;
        if(okPressed)
        {
            exp = getActionOk();
        }
        else
        {
            exp = getActionCancel();
        }
        // redirect to standard action
        setActionExpression(exp);
        queueEvent(new ActionEvent(this));
    }
}

In order to make use of of this you need two attributes (actionOk and actionCancel) which use Method Expressions (setter and getter). Those have to be configured by a ComponentHandler as you did for the action-attribute.

Upvotes: 1

Related Questions