【问题标题】:Strategy for cross field validation in VaadinVaadin 中的跨字段验证策略
【发布时间】:2014-04-25 07:34:02
【问题描述】:

Field validation 在 Vaadin 中工作很简单,但是你不能用它来检查字段之间的关系(例如,加入日期必须在离开日期之前),这真的很烦人。所以我使用JSR 303 添加了标准类级别验证,它可以做到这一点。这很好用。

但我只能在提交字段后执行此跨字段验证。这意味着 bean 已经包含所有字段更改,如果出现验证问题,我需要在字段提交之前“返回”到有效状态(或以某种方式强制 bean 的“重新加载”),否则我坚持所做的更改,例如如果用户决定取消编辑操作。

当然,我之前可以保存所有字段内容,并手动重置状态,但鉴于 Vaadin 在简单字段验证的情况下所做的完全相同,我想重用该机制。但是看着 Vaadin 代码,我不是很自信我能弄清楚该做什么,并且做得对。

请给我一些提示,如何在不重新发明轮子的情况下处理这个问题。

【问题讨论】:

    标签: java validation vaadin bean-validation vaadin7


    【解决方案1】:

    您可以将提交处理程序添加到您的FieldGroup。这使您可以在承诺之前/之后检查:

    binder.addCommitHandler(new CommitHandler() {
    
        @Override
        public void preCommit(CommitEvent commitEvent) throws CommitException {
            // TODO throw new CommitException() if your validation fails
        }
    
        @Override
        public void postCommit(CommitEvent commitEvent) throws CommitException {
            // TODO throw new CommitException() if your validation fails
        }
    });
    

    所以应该可以“跨领域”验证。

    【讨论】:

      【解决方案2】:

      您可以尝试通过实现 Validator 接口来创建自己的验证器:

      final TextField field = new TextField("Name");
      field.addValidator(new MyValidator());
      
      class MyValidator implements Validator {
                  @Override
                  public void validate(Object value) throws InvalidValueException {
                      if (!isValid(value))
                          throw new InvalidValueException("fail");
                  }
      
                  @Override
                  public boolean isValid(Object value) {
                      //some code
                  }
      }
      

      【讨论】:

      • 这个是单字段验证,但是我需要跨字段验证,根据不同字段之间的关系检查一些属性。
      • 你可以在validator中实现任何逻辑,包括检查表单中其他字段的值。
      【解决方案3】:

      确保使用com.vaadin.data.fieldgroup.FieldGroup 绑定已编辑的数据项(您需要将其保存在com.vaadin.data.Item 实例中)。 FieldGroup 将为您管理验证过程;您只需要设置适当的验证器。默认情况下,FieldGroup 是缓冲的(事务性的),因此通过显式调用 FieldGroup 的 discard() 方法或通过失败的验证,您编辑的项目的原始数据将被恢复。

      您可以在Book of Vaadin 中阅读有关 FieldGroups 的信息。

      【讨论】:

      • 我就是这样做的,但 Vaadin 不支持 类级别(或“跨域”)验证,至少我不知道。
      • 没错,您需要编写自己的跨字段验证器,该验证器需要考虑多个字段,如 user3551612 所述。关于将无效数据重置为其最后一个有效状态的问题,您可以在字段验证失败后调用 FieldGroup 上的 discard() 以恢复任何更改的值,或者您仅丢弃表单中受影响的字段。跨度>
      【解决方案4】:

      我在使用 Vaadin 时遇到了类似的问题,尤其是在尝试运行动态验证(不提交)以及想要显示字段上下文错误时。问题是当一个字段改变时,它的验证器被调用,而不是其他字段的验证器。通过跨字段验证,当另一个字段更改时,可以验证/取消验证一个字段。

      因此,一个解决方案是操纵字段的“componentError”成员。在此示例中,验证包括确保提供了两个字段中的至少一个:

          final Validator atLeastOneFieldValidator = new Validator() {
              @Override
              public void validate(final Object value) throws InvalidValueException {
                  if (!crossValidate()) {
                      throw new InvalidValueException("");
                  }
              }
          };
          field1.addValidator(atLeastOneFieldValidator);
          field2.addValidator(atLeastOneFieldValidator);
      

      我的“crossValidate”方法:

      private boolean crossValidate() {
          if (Strings.isNullOrEmpty(field1.getValue()) && Strings.isNullOrEmpty(field2.getValue())) {
              final String error = "At least 1 field must be filled";
              field1.setComponentError(new UserError(error));
              field2.setComponentError(new UserError(error));
              return false;
          }
          field1.setComponentError(null);
          field2.setComponentError(null);
          return true;
      }
      

      但不幸的是,这还不够(我无法解释原因),因为当我的两个字段出现错误并且我填写其中一个时,错误应该在它们两个上消失......但它只会从一个中消失我修改了。也许这是 Vaadin 中的一个错误。因此,除了验证器之外,我还必须在同一模型上创建一个 ValueChangeListener:

          final Property.ValueChangeListener atLeastOneField = new Property.ValueChangeListener() {
              @Override
              public void valueChange(final Property.ValueChangeEvent event) {
                  crossValidate();
              }
          };
          field1.addValueChangeListener(atLeastOneField);
          field2.addValueChangeListener(atLeastOneField);
      

      我不知道是否有更好的方法来做到这一点,因为它充满了变通方法。如果有人有更好的答案,我也想看看!尽管提出了新问题,但我将其发布为答案。

      【讨论】:

        【解决方案5】:

        在进行交叉验证时,同样重要的是要记住,您可以通过简单地将字段标记为脏来强制对其进行重新验证。因此,以下示例将显示当前字段的验证,并强制存储的字段重新验证其内容。 (我还没有验证立即/缓冲对这个策略的影响......但我不明白为什么这些会对这个解决方案产生任何负面影响)

        注意:以下示例是用 Groovy 编写的

        private class EqualsFieldValidator implements Validator {
        
            final AbstractTextField field
        
            final String message
        
            public EqualsFieldValidator(AbstractTextField field, String message = 'fields are not equal') {
                this.field = field
                this.message = message
            }
        
            @Override
            void validate(Object value) throws InvalidValueException {
                field.markAsDirty()
                if (value != field.value) {
                    throw new InvalidValueException(message)
                }
            }
        }
        

        【讨论】:

          【解决方案6】:

          This might be useful 它不是 JSR-303,接近它,但差异是故意的。与 JSR-303 一样,它由带注释的字段驱动,并且注释大多是相同的名称。 它使用后台规则引擎进行跨字段验证以及没有规则的简单验证(范围检查等)。 (我知道这是一个老问题,但我刚刚偶然发现它)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-07-06
            • 2023-03-23
            • 1970-01-01
            • 2014-11-05
            • 2012-09-26
            • 2011-01-17
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多