根据 f:viewParam 有条件地调用 f:viewAction

Posted

技术标签:

【中文标题】根据 f:viewParam 有条件地调用 f:viewAction【英文标题】:Invoke f:viewAction conditionally based on f:viewParam 【发布时间】:2014-10-04 04:08:57 【问题描述】:

我们有一个设置,我们将不同的可选视图参数传递给 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 中,但我如何调整代码以使第一个场景工作?

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

【问题讨论】:

【参考方案1】:

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

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

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

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

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

您想测试在应用请求值阶段保证可用的其他东西。最好的候选者是实际的请求参数本身。所有“普通”请求参数都在implicit EL object #param 可用的EL 范围内,它引用Map&lt;String, String&gt;,参数名称为键。

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

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

更新以解决 &lt;f:viewAction if&gt; 的不直观行为,JSF 实用程序库 OmniFaces 将从 2.2 版开始提供 &lt;o:viewAction&gt;,其 if 仅在 调用应用程序 阶段,就在广播动作事件之前。这个技巧是通过简单地扩展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"/>

【讨论】:

以上是关于根据 f:viewParam 有条件地调用 f:viewAction的主要内容,如果未能解决你的问题,请参考以下文章

如何在不调用 <f:viewparam> 转换器的情况下调用 setter?

何时调用 <f:metadata><f:viewParam> 的设置器

如果位于 template.xhtml 中,JSF f:viewParam 不调用 setter

f:viewParam 具有多个值

当 URL 中没有参数时,将 f:viewParam 属性设置为 null

<f:viewParam> 的验证/转换错误不会本地化到 <f:view locale>,而是本地化到默认语言环境