我遇到了以下问题:在一个页面中,我列出了我的应用程序的所有用户,并为每个用户设置了一个“编辑”按钮,这是与?id=<userid>
的“GET”链接。
编辑页面的元数据中包含<f:viewParam name="id" value="#{editUserBean.id}"/>
。
如果我输入了一些输入错误并提交(我使用CDI Weld Bean验证),页面会再次显示,但我丢失了URL中的?id=...
,因此丢失了我正在编辑的用户的用户ID
我看过JSF validation error, lost value中描述的类似问题,但是输入隐藏的解决方案(或更糟糕的是,使用tomahawk,看起来有点过分)需要大量的uggly代码。
我尝试在CDI中添加“对话”,但它确实有效,但看起来对我来说太过分了。
在JSF中是否存在一个简单的解决方案,以便在出现验证错误时保留视图参数?
[我的环境:Tomcat7 + MyFaces 2.1.0 + Hibernate Validator 4.2.0 + CDI(Weld)1.1.2]
答案 0 :(得分:7)
有趣的案例。对于每个人来说,以下最小代码重现了这一点:
的facelet:
<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;
}
}
如果您使用viewparam.xhtml?id=12
调用Facelet,它将在屏幕上显示12
。如果您输入有效的内容,例如aaaaa
,id将从URL中消失,但会一直显示在屏幕上(拥有ui组件的有状态性质)。
但是......正如OP所提到的,只要发生任何验证器错误(例如输入a
),ID就会永久丢失。之后输入有效输入不会带回来。这几乎看起来像一个bug,但我尝试了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()
为真,它将尝试从模型(支持bean)获取值。但由于这个bean是请求作用域,因此该请求没有任何值,因为验证刚刚失败,因此没有设置任何值。实际上,UIViewParameter
的有状态值会被支持bean作为默认值返回的任何内容覆盖(通常为null,但它当然取决于您的bean)。
一个解决方法是制作你的bean @ViewScoped
,这通常是一个更好的范围(我假设你使用参数从服务中获取用户,并且可能没有必要一遍又一遍地执行此操作每一次回发)。
另一种方法是创建自己的UIViewParameter
版本,如果验证失败(基本上所有其他UIInput
组件都没有),则不会尝试从模型中获取值。
答案 1 :(得分:1)
实际上你没有松开view参数。 f:viewParam是有状态的,所以即使它不在URL中,它仍然存在。只需在setter中放置一个断点或system.out来查看param。
(如果您在viewParam无状态状态上google,您会找到更多信息)
答案 2 :(得分:0)
我的申请表中也是如此。我切换到@ViewAccessScoped,它允许更优雅的实现。
答案 3 :(得分:0)
<f:metadata>
<f:viewParam id="id" name="id" value="#{baen.id}"/>
</f:metadata>
或者当您第一次从url获取参数时,将其保存在会话映射中并继续使用该映射,并在保存/或更新表单干净映射后。
答案 4 :(得分: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>