8

我面临以下问题:在一个页面中,我列出了我的应用程序的所有用户,并且每个用户都有一个“编辑”按钮,这是一个带有?id=<userid>.

编辑页面有一个<f:viewParam name="id" value="#{editUserBean.id}"/>in 元数据。
如果我犯了一些输入错误并提交(我使用 CDI Weld Bean 验证),页面会再次显示,但我丢失了?id=...URL 中的内容,因此丢失了我正在编辑的用户的用户 ID。

我已经查看了JSF validation error, lost value中描述的类似问题,但是使用 inputhidden(或更糟糕的是,使用 tomahawk,看起来有点矫枉过正)的解决方案需要大量难看的代码。

我试过用 CDI 添加一个“对话”,它正在工作,但对我来说,这似乎太过分了。

JSF 中是否存在一个简单的解决方案来在验证错误的情况下保留视图参数?

[我的环境:Tomcat7 + MyFaces 2.1.0 + Hibernate Validator 4.2.0 + CDI(Weld) 1.1.2]

4

5 回答 5

7

有趣的案例。对于每个人,以下最小代码重现了这一点:

小面:

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
>

    <f:metadata>
        <f:viewParam id="id" name="id" value="#{viewParamBean.id}"/>
    </f:metadata>

    <h:body>

        <h:messages />

        #{viewParamBean.id} <br/>

        <h:form>
            <h:inputText value="#{viewParamBean.text}" >
                <f:validateLength minimum="2"/>
            </h:inputText>

            <h:commandButton value="test" action="#{viewParamBean.actionMethod}"/>
        </h:form>

    </h:body>
</html>

豆:

@ManagedBean
@RequestScoped
public class ViewParamBean {

    private long id;    
    private String text;

    public void actionMethod() {

    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }    
}

如果您用它调用 Facelet,viewparam.xhtml?id=12它将显示在12屏幕上。如果您随后输入了一些有效的内容,例如aaaaa,id 将从 URL 中消失,但会继续显示在屏幕上(由于 ui 组件的有状态特性)。

但是...正如 OP 所提到的,一旦发生任何验证器错误(例如输入a),该 ID 将永久丢失。之后输入有效输入不会将其恢复。这几乎看起来像一个错误,但我尝试了 Mojarra 2.1 和 Myfaces 2.1 并且两者都有相同的行为。

更新:

经过一番检查,问题似乎出在 `UIViewParameter' (Mojarra) 的这种方法中:

public void encodeAll(FacesContext context) throws IOException {
    if (context == null) {
        throw new NullPointerException();
    }

    // if there is a value expression, update view parameter w/ latest value after render
    // QUESTION is it okay that a null string value may be suppressing the view parameter value?
    // ANSWER: I'm not sure.
    setSubmittedValue(getStringValue(context));
}

然后更具体地说是这种方法:

public String getStringValue(FacesContext context) {
    String result = null;
    if (hasValueExpression()) {
        result = getStringValueFromModel(context);
    } else {
        result = (null != rawValue) ? rawValue : (String) getValue();
    }
    return result;
}

因为hasValueExpression()是 true,它会尝试从模型(支持 bean)中获取值。但是由于这个 bean 是请求范围的,它不会对该请求有任何值,因为验证刚刚失败,因此没有设置任何值。实际上,有状态的值UIViewParameter会被支持 bean 返回的任何默认值覆盖(通常为 null,但它当然取决于您的 bean)。

一种解决方法是制作您的 bean @ViewScoped,这通常是一个更好的范围(我假设您使用该参数从服务中获取用户,并且可能没有必要在每次回发时一遍又一遍地这样做)。

另一种选择是创建自己的版本,如果验证失败(基本上所有其他组件都这样做)UIViewParameter,则不会尝试从模型中获取值。UIInput

于 2011-07-31T16:42:19.183 回答
1

您实际上并没有丢失 view 参数。f:viewParam 是有状态的,所以即使它不在 URL 中,它仍然存在。只需在绑定到查看参数的设置器中放置一个断点或 system.out 即可。

(如果你在 viewParam stateless stateful 上谷歌,你会发现更多信息)

于 2011-07-30T22:21:03.797 回答
0

我的应用程序中也有同样的情况。我切换到@ViewAccessScoped,它允许更优雅的实现。

于 2011-08-21T19:20:29.273 回答
0
 <f:metadata>
        <f:viewParam id="id" name="id" value="#{baen.id}"/>
    </f:metadata>

或者当您第一次从 url 获取参数时,将其保存在会话映射中并继续从该映射中使用,并在保存/或更新表单清理映射之后。

于 2013-12-21T14:00:55.743 回答
0

这很棘手,但您可以尝试使用 History API 恢复视图参数:

<?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"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
    <f:metadata >
        <f:viewParam name="param1" value="#{backingBean.viewParam1}" />
        <f:viewParam name="param2" value="#{backingBean.viewParam2}" />
        <f:viewAction action="#{view.viewMap.put('queryString', request.queryString)}" />
    </f:metadata>
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <ui:fragment rendered="#{facesContext.postback}" >  
            <script type="text/javascript">
                var url = '?#{view.viewMap.get('queryString')}';
                history.replaceState({}, document.title, url);
            </script>
        </ui:fragment>
        <h:form>
            <h:inputText id="name" value="#{backingBean.name}" />
            <h:message for="name" style="color: red" />
            <br />
            <h:commandButton value="go" action="#{backingBean.go}" />
        </h:form>
        <h:messages globalOnly="true" />
    </h:body>
</html>
于 2016-04-04T02:00:04.103 回答