【问题标题】:Custom Validator for p:selectOneMenu make it not working rendering / not rendering other componentsp:selectOneMenu 的自定义验证器使其无法渲染/无法渲染其他组件
【发布时间】:2021-09-28 03:00:20
【问题描述】:

我有这个选择:

<p:selectOneMenu  value="#{bean.val}">
    <f:selectItem itemValue="X" itemLabel="Select" />
    <f:selectItems value="#{bean.getVals()}" />
    <p:ajax update="wrapper" />
</p:selectOneMenu>

这是它更新的包装器:

<p:panel id="wrapper">
    <p:panel rendered="#{bean.val == 'A' or bean.val == 'B'}">
        <!-- insert your code here -->
    </p:panel> 
</p:panel>

包装器在选择之外,处于同一级别。

一开始,一切都好。包装器已隐藏。

如果我选择“A”然后选择“C”,例如,包装消失。 但是,如果我再次选择'A'或'B'和'X'(第一个“选择”,选择),包装器确实不会消失!

我在bean.valsetter 中放置了一个断点。为 第一个 的所有选项 BUT 调用的设置器,调试器内的值与 前一个 相同!

但是!如果我删除 验证器,它工作

这是验证器:

@FacesValidator(value="requiredSelect")
public class RequiredSelect implements Validator {
    protected MessageUtil messageUtil = new MessageUtil();
    
    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        String val = (String) value;
        
        if ("X".equals(val)) {
            FacesMessage msg = this.messageUtil.getDefaultValidationError();
            throw new ValidatorException(msg);
        }
    }
}

之前我使用过另一个验证器:

@FacesValidator(value="requiredString")
public class RequiredString implements Validator {
    protected MessageUtil messageUtil = new MessageUtil();
    
    @Override
    public void validate(
        FacesContext context, 
        UIComponent component, 
        Object value
    ) throws ValidatorException {
        String val = (String) value;
        
        if (val == null || val.trim().isEmpty()) {
            FacesMessage msg = this.messageUtil.getDefaultValidationError();
            throw new ValidatorException(msg);
        }
    }
}

但它确实工作,因为在菜单更改时 空字符串 没有保存到支持 bean。因此,例如,如果您选择“A”然后返回“”(选择)并尝试提交表单,则选择会发出错误信号, 的选择返回为 'A'!所以虚假的“X”值。

我正在使用 Primefaces 3.4.1 和 Mojarra 2.1.7

【问题讨论】:

    标签: jsf primefaces


    【解决方案1】:

    您的问题有点 XY 问题。您正在尝试使用X 选择项来破解创建所需的p:selectOneMenu 的方法。这需要您创建一个验证器。如果您只是将p:selectOneMenu 设为必需,并将null 用作占位符的itemValue,则无需处理X,因此您不需要验证器。

    选择null 将导致验证失败。因此,一旦做出选择,您就不再希望呈现 null 占位符。通常会使用p:selectOneMenuhideNoSelectionOption 属性。但这在 PrimeFaces 3.4.1 中不可用。由于您不想升级,因此可以通过为 SelectOneMenu 创建自定义渲染器来添加此行为。

    创建一个新类,比如my.custom.MySelectOneMenuRenderer,然后扩展SelectOneMenuRenderer。在这个你想@Override encodeInput 方法类似于:

    protected SelectItem selectMeOption = new SelectItem(null, "Select");
    
    @Override
    protected void encodeInput(
        FacesContext context, SelectOneMenu menu, String clientId,
        List<SelectItem> selectItems, Object values, Object submittedValues,
        Converter converter
    ) throws IOException {
        String classes = menu.getStyleClass();
        Pattern pattern = Pattern.compile("\\bnormalSelect\\b");
        Matcher matcher = pattern.matcher(classes);
        boolean isNormal = matcher.find();
        Object value = menu.getValue();
        
        // If there's not a class "normalSelect" and no value is set, add the
        // null option as the first item
        if (!isNormal) {
            if (value == null) {
                selectItems.add(0, selectMeOption);
            }
            else {
                SelectItem firstOption = selectItems.get(0);
                
                if (selectMeOption.equals(firstOption)) {
                    selectItems.remove(0);
                }
            }
        }
        
        super.encodeInput(
            context, menu, clientId, selectItems, values, submittedValues,
            converter
        );
    }
    

    将您的自定义渲染器添加到 faces-config.xml 中的 render-kit 部分,例如:

    <render-kit>
      <renderer>
        <component-family>org.primefaces.component</component-family>
        <renderer-type>org.primefaces.component.SelectOneMenuRenderer</renderer-type>
        <renderer-class>my.custom.MySelectOneMenuRenderer</renderer-class>
      </renderer>
    </render-kit>
    

    查看示例:

    <p:selectOneMenu value="#{bean.val}" layout="pageDirection">
        <f:selectItems value="#{bean.vals()}" />
        <p:ajax update="#{empty bean.val ? '@this' : ''}" />
    </p:selectOneMenu>
    

    有关工作示例,请参阅:https://github.com/jepsar/primefaces-test/blob/so_68457571/src/main/webapp/test.xhtml

    另见:

    【讨论】:

    • 它有效。你忘了说重新申请&lt;p:ajax update="#{empty bean.val ? '@this' : ''}" /&gt; ;-)
    • 太棒了!顺便说一句,自定义属性是控制渲染器行为 IMO 的一种更优雅的方式。而且你可以更好地做到&lt;p:ajax update="@this" rendered=#{empty bean.val}" /&gt;(在设置值时保存你的Ajax请求)。
    【解决方案2】:

    请查看 JSF 生命周期,例如: https://www.torsten-horn.de/img/JSF-Lifecycle.gif

    您会注意到,如果您抛出“验证器异常”,则会跳过“更新模型值”阶段。 (处理事件转发以呈现响应)。

    因此,您认为在验证器中无效的值“X”永远不会发送到 bean,因此包装器不会消失。

    你真正想要的是:

    • 定义适当的“NoSelect-Option”
    • 将该字段设为“必填”。

    然后 - 在提交时 - 必填字段将被视为空并在您需要的地方抛出验证器异常。

    ps.:绕过此“问题”的一种巧妙方法是在selectOneMenu 上使用hideNoSelectionOption - 这样,表单以“请选择”开头,但用户无法切换回“请选择”,一旦他做出选择:

    <p:selectOneMenu  value="#{bean.val}" hideNoSelectionOption="#{not empty bean.val}">
        <f:selectItem itemValue="#{null}" itemLabel="Select" noSelectionOption="true" />
        <f:selectItems value="#{bean.getVals()}" />
        <p:ajax update="@this,wrapper" process="@this" />
    </p:selectOneMenu>
    

    已更新,将@this 添加到p:ajax

    PS:这不适用于没有 hideNoSelectionOption 的旧版本 Primefaces

    【讨论】:

    • @JasperdeVries 否,NoSelect-Option 有效才能被选中。当提交表单并且该字段是“必需的”时,它只会导致验证错误。这就是它应该如何工作的方式。
    • 问题是我无法自定义require="true"消息
    • @MarcoSulla 你可以。使用required="true" requiredMessage="This field is required, bro!"
    • @JasperdeVries 不,“必需”验证确实发生在提交时,而不是在回发时。原因是“他的”验证器导致了这个问题,因为他正在评估他“自己设计的”要求验证在回发时,它只应该在提交时发生。
    • 我不想添加相同的 requiredMessage="这个字段是必需的,兄弟!"对于 ALL 输入,适用于 One Source of Truth 原则。我不能更改默认消息吗?
    【解决方案3】:

    好的,这是我的解决方案,感谢dognoseanswer第一部分:

    1. 将输入更改为:
    <p:selectOneMenu value="#{bean.val}" required="true">
        <f:selectItem itemValue="" itemLabel="Select" />
        <f:selectItems value="#{bean.getVals()}" />
        <p:ajax update="wrapper" />
    </p:selectOneMenu>
    

    不需要itemValue="#{none}",因为默认情况下空字符串会被jboss或primefaces自动转换为空字符串,我不记得了。也不需要noSelectionOption="true"(我不明白它的范围......即使我不说验证也有效)

    1. 添加到包my.package 文件messages.properties,包含以下内容:
    javax.faces.component.UIInput.REQUIRED = Error
    javax.faces.component.UIInput.REQUIRED_detail = One or more fields are missing or wrong. Please check the form
    
    1. 然后我添加了一个p:messages,就像这样:
    <p:messages id="messages" closable="true" showDetail="true" />
    
    1. 然后,我使用this trick 删除了重复的消息。

    【讨论】:

    • 尝试先选择一个值,然后选择空的“选择”选项。
    • @JasperdeVries 我试过了,它有效。我用“选择”提交,它是红色的。我更改了一个值,我提交了它是“白色的”。然后我再次更改为“选择”并提交,它返回红色。
    • 这种情况下包装器是否也更新了?
    • @JasperdeVries Uops。不......我尝试 null 和/或 noSelectionOption :D
    • @JasperdeVries:我尝试了 null 和 noSelectionOption 的所有组合,什么都没有。包装器没有更新,可能是因为未调用设置器
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-19
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 2018-02-17
    相关资源
    最近更新 更多