Vaadin中的交叉场验证策略

时间:2014-04-25 07:34:03

标签: java validation vaadin bean-validation vaadin7

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

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

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

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

6 个答案:

答案 0 :(得分:4)

您可以为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
    }
});

所以应该可以"交叉领域"验证

答案 1 :(得分:3)

您可以尝试通过实现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
            }
}

答案 2 :(得分:1)

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

您可以在Book of Vaadin

中阅读有关FieldGroups的信息

答案 3 :(得分:1)

我遇到了与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);

我不知道是否有更好的方法可以做到这一点,因为它充满了变通方法。如果有人有更好的答案,我也希望看到它!我发布它作为答案虽然提出了新的问题。

答案 4 :(得分:0)

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

注意:以下示例是用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)
        }
    }
}

答案 5 :(得分:-1)

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