从 bean 访问 PrimeFaces 命令按钮以添加动作侦听器

accessing PrimeFaces command button from bean to add action listener

我在 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>

我正在尝试动态分配不同的 actionListener。如果用户想要插入一些新记录,我希望它调用插入方法。如果用户想要更新现有记录,则应调用 update 方法。

现在我正在尝试这样做:

@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));

}

我在该行收到一个空指针异常:

button.addActionListener(new MethodExpressionActionListener(
        methodExpression));

我做错了什么?还有另一种方法可以完成我想做的事情吗?我正在使用 JSF 2.2、PrimeFaces 5.3 和 OmniFaces 1.11。

findComponent() 将客户端 ID 作为参数而不是组件 ID。客户端 ID 正是与相关组件关联的生成的 HTML id 属性的值。在命令按钮的情况下,通常父组件 ID <h:form> 是前置的,由默认为 :.

的命名容器分隔符分隔

鉴于此,

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

客户端 ID 将是 form:save

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

另请参阅有关识别和使用客户端 ID 的相关问题:How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"


与具体问题无关,在Java端操作组件树是一种不好的做法。您最好为此继续使用 XHTML+XML,这对于 declaring/defining 树结构而言是更多的自我记录。您可以使用 JSTL 标签动态构建视图(注意:这不同于使用 rendered 属性动态渲染视图!)。

例如

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

另见 JSTL in JSF2 Facelets... makes sense?

更重要的是,您可以将 #{bean.existing} 作为方法参数传递。

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

如果 #{bean.existing} 引用与 #{bean.save} 相同的 bean,那么这两种方法都被承认有点奇怪。您可以在 #{bean.save} 本身内部检查它。

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

更进一步,这不是前端层的责任,而是服务层的责任。您将整个实体传递给服务层,服务层会根据 PK 检查它是否存在。

if (entity.getId() == null) {
    // INSERT
} else {
    // UPDATE
}