我们有一个设置,其中我们将不同的可选 View 参数传递给 JSF 页面以及在设置参数后要处理的后续 View 操作。一个非常简单的例子如下所示:

page.xhtml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://xmlns.jcp.org/jsf/html"
  xmlns:f="http://xmlns.jcp.org/jsf/core">

<f:view>
    <f:metadata>
        <f:viewParam name="a" value="#{page.a}"/>
        <f:viewAction action="#{page.populateA()}" if="#{not empty page.a}"/>

        <f:viewParam name="b" value="#{page.b}"/>
        <f:viewAction action="#{page.populateB()}"/>
    </f:metadata>

    <h:outputLabel value="#{page.message}"/>
</f:view>
</html>

页面
import javax.faces.view.ViewScoped;
import javax.inject.Named;

@ViewScoped
@Named
public class Page {

    private String a;

    private String b;

    private String message;

    public String getA() {
        return a;
    }

    public void setA(String a) {
        this.a = a;
    }

    public String getB() {
        return b;
    }

    public void setB(String b) {
        this.b = b;
    }

    public String getMessage() {
        return message;
    }

    public void populateA() {
        this.message = "Param a given: " + this.a;
    }

    public void populateB() {
        if (this.b != null) {
            this.message = "Param b given: " + this.b;
        }
    }
}

现在,不同之处在于 a 的处理不起作用 (page.xhtml?a=123),而 b 的处理就像一个魅力 (page.xhtml?b=123) - 尽管我以为我只是移动了从 Java 到 JSF 的空检查。在可读性方面,我更愿意在 Java 中省略额外的空检查,并将 View 参数处理完全位于 JSF 中,但是如何调整代码以使第一个场景起作用?

编辑 根据 What is the purpose of rendered attribute on f:viewAction? 的公认答案,if 本身有效,所以我怀疑执行顺序错误(首先评估 Action 条件,然后将值应用于模型)。

最佳答案

<f:viewAction if> 属性基本上是一个重命名的 <h:xxx rendered> 属性(无论是否更清楚,我都会留在中间,它至少有一个 bug in autogenerated documentation 作为结果;它列出 rendered 而不是 if ),因此在其中具有完全相同的生命周期应用请求值阶段与所有其他 UI 组件一样。此阶段调用 View 中所有 UI 组件的 UIComponent#decode()。对于 UIViewAction <f:viewAction>标签后面的UI组件类, decode() 描述如下:

所以,是的,对于“错误的执行顺序”,您确实是对的。在 Apply Request Values 阶段,它检查 if 属性,如果它评估 false ,则不会进行解码并且不会将 Action 事件排队。另请参阅 UIViewAction source code 的第 644 行,它在 isRendered() 内部测试 decode() 并且如果 false 则不继续。在您的情况下,您在 #{page.a} 中检查的 <f:viewAction if> 仅在更新模型值阶段可用,该阶段在应用请求值阶段之后。
您想测试在应用请求值阶段保证可用的其他内容。最好的候选者是实际的请求参数本身。所有“普通”请求参数都在 implicit EL object #{param} 可用的 EL 范围内, Map<String, String> <f:viewAction if> 以参数名称作为键引用 OmniFaces
所以,总而言之,这应该做:

<f:viewParam name="a" value="#{page.a}"/>
<f:viewAction action="#{page.populateA}" if="#{not empty param.a}"/>

更新 以解决 <o:viewAction> 的不直观行为,JSF 实用程序库 ojit_a 将自 2.2 版起提供一个 if,其 UIViewAction 仅在调用应用程序阶段评估,就在广播 Action 事件之前。这个技巧是通过简单地扩展 broadcast() 组件并覆盖其 isRendered() 和 ojit_code 来完成的,如下所示:
@Override
public void broadcast(FacesEvent event) throws AbortProcessingException {
    if (super.isRendered()) {
        super.broadcast(event);
    }
}

@Override
public boolean isRendered() {
    return !isImmediate() || super.isRendered();
}
这使您可以更直观地使用标签:
<f:viewParam name="a" value="#{page.a}"/>
<o:viewAction action="#{page.populateA}" if="#{not empty page.a}"/>

关于jsf - 调用 f :viewAction conditionally based on f:viewParam,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25241437/

10-13 06:15