1

I have the following command button in the view with ID "save":

        <p:panel style="border:none;text-align:left;margin:0;">

            <p:commandButton value="Save Document" id="save" icon="fa fa-save"
                disabled="#{dIGRCController.digrc.qconce == '020'}">

                <f:param name="validate" value="true" />

            </p:commandButton>

            <p:commandButton value="Clear" icon="fa fa-undo"></p:commandButton>

        </p:panel>

I am trying to dynamically assign a different actionListener. If the user wants to INSERT some new record, I want it to call the insert method. If the user wants to update an existing record, it should call the update method.

Right now I am trying to do this:

@PostConstruct
public void init() {

    // setting the action listener of the Save Document button
    UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();

    // UIComponent button = viewRoot.findComponent("save");

    CommandButton button = (CommandButton) viewRoot.findComponent("save");

    FacesContext context = FacesContext.getCurrentInstance();

    MethodExpression methodExpression = context
            .getApplication()
            .getExpressionFactory()
            .createMethodExpression(context.getELContext(),
                    "#{dIGRCController.updateDocument}", null,
                    new Class[] { DIGRCController.class });

    button.addActionListener(new MethodExpressionActionListener(
            methodExpression));

}

I am getting a null pointer exception on the line:

button.addActionListener(new MethodExpressionActionListener(
        methodExpression));

What am I doing wrong? Is there another way to accomplish what I am trying to do? I am using JSF 2.2, PrimeFaces 5.3 and OmniFaces 1.11.

Erick
  • 823
  • 16
  • 37
  • 1
    `findComponent()` takes client ID as argument, not component ID. But this all looks overcomplicated. Defining the view usually happens in XHTML file not in Java class. Manipulating component tree in Java side is a poor practice. You can better use JSTL for that so you can just keep using XHTML which is so much more self-documenting as to defining components/attributes than Java code. – BalusC Apr 10 '16 at 20:23
  • Got it. I was also thinking on having two command buttons: Update and another one Insert. And rendering one given some condition for update and the other one with some insert condition. What do you think about that design @BalusC? – Erick Apr 10 '16 at 20:26
  • Depends on where the condition comes from, but that is indeed one way. If the condition were view build time based, you could use a single command button with a ``, basically doing the same as your Java code attempt. – BalusC Apr 10 '16 at 20:31
  • Perfect. I am going to do it that way then. Thanks! – Erick Apr 10 '16 at 20:34

1 Answers1

1

The findComponent() takes a client ID as argument not a component ID. The client ID is exactly the value of the generated HTML id attribute associated with the component in question. In case of a command button, usually the component ID of the parent <h:form> is prepended, separated by the naming container separator character which defaults to :.

Given this,

<h:form id="form">
    <p:commandButton id="save" ... />
</h:form>

the client ID would be form:save.

CommandButton button = (CommandButton) viewRoot.findComponent("form:save");

See also this related question as to identifying and using client ID: How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"


Unrelated to the concrete problem, manipulating the component tree in Java side is a poor practice. You'd better keep using XHTML+XML for this which is so much more self-documenting as to declaring/defining tree structures. You can use JSTL tags to dynamically build the view (note: this is different from dynamically rendering the view using rendered attribute!).

E.g.

<p:commandButton ... action="#{bean.save}">
    <c:if test="#{bean.existing}">
        <f:actionListener binding="#{bean.needsUpdate()}" />
    </c:if>
</p:commandButton>

See also JSTL in JSF2 Facelets... makes sense?

Even more, you could just pass #{bean.existing} as method argument.

<p:commandButton ... action="#{bean.save(bean.existing)}" />

Both approaches are in turn admittedly kind of weird if #{bean.existing} refers the same bean as #{bean.save}. You could just check for that inside #{bean.save} itself.

public void save() {
    if (existing) {
        // UPDATE
    } else {
        // INSERT
    }
}

Going further on that, this is IMO not the responsibility of frontend layer, but of the service layer. You pass the whole entity to the service layer which in turn checks based on PK if it's existing or not.

if (entity.getId() == null) {
    // INSERT
} else {
    // UPDATE
}
Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Awesome! I love that solution. Is a lot better than what I said about creating two buttons and rendering! Thanks @BalusC! – Erick Apr 10 '16 at 20:57