3

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

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) - 尽管我认为我只是移动了 null-检查从 Java 到 JSF。就可读性而言,我宁愿在 Java 中省略额外的空检查,并让视图参数处理完全位于 JSF 中,但是如何调整代码以使第一个场景工作?

编辑根据什么是 f:viewAction 上的渲染属性的目的的公认答案?if本身有效,所以我怀疑执行顺序错误(首先评估动作条件,然后将值应用于模型)。

4

1 回答 1

7

<f:viewAction if>属性基本上是一个重命名的<h:xxx rendered>属性(无论是否更清楚,我都会留在中间,因此它在自动生成的文档中至少有一个错误;它列出rendered而不是if),因此在除其他外具有完全相同的生命周期像所有其他 UI 组件一样应用请求值阶段。此阶段调用UIComponent#decode()视图中的所有 UI 组件。对于标签UIViewAction后面的UI组件类,描述如下:<f:viewAction>decode()

如果满足以下任一条件,请不要采取任何措施:

  • 当前请求是回发,并且实例已配置为不在回发上操作。见isOnPostback()

  • 属性中所述的条件if评估为假。看isRendered()

所以,是的,关于“错误的执行顺序”,你确实是对的。在应用请求值阶段,它会检查if属性,如果它评估false,则不会进行解码,并且操作事件不会排队。另请参阅UIViewAction 源代码的第 644 行,其中测试isRendered()insicedecode()并且不继续 if false。在您的情况下,#{page.a}您在 中签入的<f:viewAction if>仅在更新模型值阶段可用,该阶段在应用请求值阶段之后。

您想测试在应用请求值阶段保证可用的其他内容。最好的候选者是实际的请求参数本身。所有“普通”请求参数都在隐式 EL 对象 #{param}可用的 EL 范围内,该对象引用Map<String, String>以参数名称为键的 a。

所以,总而言之,这应该做:

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

更新以解决不直观的行为<f:viewAction if>,JSF 实用程序库OmniFaces将从 2.2 版开始提供仅在调用应用程序阶段(即在广播动作事件之前)<o:viewAction>对其if进行评估的功能。这个技巧是通过简单地扩展组件并覆盖它的和来完成的,如下所示:UIViewActionbroadcast()isRendered()

@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}"/>
于 2014-08-11T20:46:17.623 回答