基于f:viewParam有条件地调用f:viewAction



我们有一个设置,其中我们将不同的可选视图参数传递给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)—尽管我认为我只是将空检查从Java移到了JSF。在可读性方面,我宁愿在Java中省略额外的空检查,并将视图参数处理完全置于JSF中,但是我如何调整代码以使第一个场景工作呢?

EDIT根据f:viewAction上呈现属性的目的是什么?, if本身工作,所以我怀疑错误的执行顺序(首先评估动作条件,然后将值应用到模型)。

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

如果满足下列条件之一,不采取任何操作:

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

  • if属性中声明的条件计算结果为false。参见isRendered()

所以,是的,关于"执行顺序错误",你确实是对的。在Apply Request Values阶段,它检查if属性,如果它评估false,那么解码将不会发生,动作事件将不会排队。参见UIViewAction源代码的第609行,它在decode()中测试isRendered(),如果是false则不继续。在您的情况下,您在<f:viewAction if>中检查的#{page.a}仅在更新模型值阶段期间可用,这是在 应用请求值阶段之后的

您想测试的东西是保证在应用请求值阶段可用。最好的候选者是实际的请求参数本身。所有"plain"请求参数在EL范围内,由隐式EL对象#{param}提供,该对象引用了一个以参数名称为键的Map<String, String>

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

<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仅在Invoke Application阶段(就在动作事件广播之前)进行评估。这个技巧是通过简单地扩展UIViewAction组件并重写其broadcast()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}"/>

相关内容

  • 没有找到相关文章

最新更新