我有这个f:viewParam,我尝试将其绑定到validate并将userId转换为Player,但得到了意外的结果。

<f:metadata>
    <f:viewParam name="userId" value="#{myBean.selectedPlayer}" converter="pConverter"
         converterMessage="Bad Request. Unknown User" required="true"
         requiredMessage="Bad Request. Please use a link from within the system" />

</f:metadata>
<h:body>
    <p:messages id="msgs"/>
    <h:form>
        <ul>
            <li><a href="index2.xhtml?userId=1">Harry</a></li>
            <li><a href="index2.xhtml?userId=2">Tom</a></li>
            <li><a href="index2.xhtml?userId=3">Peter</a></li>
        </ul>
    </h:form>
    <h:form>
        <h:panelGrid columns="2" rendered="#{not empty myBean.selectedPlayer}">

            <h:outputText value="Id: #{myBean.selectedPlayer.id}"/>

            <h:outputText value="Name: #{myBean.selectedPlayer.name}"/>

        </h:panelGrid>
    </h:form>
    <h:form id="testForm">
        <h:inputText value="#{myBean.text}"/>
        <p:commandButton value="Switch" update=":msgs testForm"/>
        <h:outputText value="#{myBean.text}" rendered="#{not empty myBean.text}"/>
    </h:form>
</h:body>

我的转换器看起来像这样
@FacesConverter(value="pConverter")
public class PConverter implements Converter {
private static final List<Player> playerList;
static{
    playerList = new ArrayList<Player>();
    playerList.add(new Player(1, "Harry"));
    playerList.add(new Player(2, "Tom"));
    playerList.add(new Player(3, "Peter"));
}

@Override
public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
    if(value == null || !value.matches("\\d+")){
        return null;
    }
    long id = Long.parseLong(value);
    for(Player p : playerList){
        if(p.getId() == id){
            return p;
        }
    }
    throw new ConverterException(new FacesMessage("Unknown userId: " + value));

}

@Override
public String getAsString(FacesContext fc, UIComponent uic, Object value) {
    if(!(value instanceof Player) || value == null){
        return null;
    }
    return String.valueOf(((Player)value).getId());
}
}

当我单击三个链接(Harry,Tom,Peter)时,转换器运行良好。它转换id并将player绑定回我的托管bean。然后,我在文本框中键入内容,然后单击Switch,它第一次正常运行时,我键入的内容显示在按钮旁边,但是随后我更改了键入的内容,再次单击Switch,然后出现错误消息Bad Request. Please use a link from within the system,这是requiredf:viewParam错误消息。如果我将f:viewParam取出,那么一切正常。出人意料的是,如果我从f:viewParam切换到o:viewParam(OmniFaces),则效果很好。

最佳答案

这是因为<f:viewParam>在每个HTTP请求上运行,也在回发上运行。对于普通的GET链接,它可以正常工作,因为您在链接中传递了该参数。对于POST表单,此操作将失败,因为您没有在按钮中传递该参数。因此,它在请求参数映射中变为null,并且required验证程序启动,因此此验证错误。

为了使<f:viewParam required="true">在POST表单上也保持满意,您基本上需要通过<f:param>在命令按钮/链接中保留初始请求参数。

<p:commandButton value="Switch" update=":msgs testForm">
    <f:param name="userId" value="#{param.userId}" />
</p:commandButton>

OmniFaces <o:viewParam> 旨在与视图作用域的bean结合使用,在isRequired() getter(source code here)中有一个附加检查:
@Override
public boolean isRequired() {
    // The request parameter get lost on postbacks, however it's already present in the view scoped bean.
    // So we can safely skip the required validation on postbacks.
    return !FacesContext.getCurrentInstance().isPostback() && super.isRequired();
}

因此,这会在每个回发时跳过required验证程序(此外,由于其无状态性质,它还会跳过在每个回发中设置模型值)。这就是为什么您没有看到验证错误并且仍然拥有正确的模型值的原因(不会在每次回发时重置该值)。

09-25 19:20