验证 vaadin 组合框自定义输入(仅限整数)

Validate vaadin combobox custom input (integers only)

我有一个带有显示整数值的组合框的输入表单。我希望用户能够向此组合框添加新的整数值。我需要 验证输入是否只有数字 (而不是字母)——如果它不是有效的整数,我想显示一条错误消息。

对于文本字段,我很快就弄明白了 - 即使在输入 "wrong" 输入时我也会收到错误消息!但是我找不到结合 addCustomValueSetListener 的组合框的解决方案。

我正在使用 Vaadin 14.1.21 和 Java JDK+JRE 1.8。

现在,如果我输入带有字母的自定义值,框下方不会显示错误消息,当我想 "save" 它/将其存储在中时,它只是默默地忽略输入值数据库。

public class MyForm extends Div {


    private TextField tf;
    tf =new TextField("TF");
    tf.setWidth("100%");
    tf.setRequired(true);
    tf.addThemeVariants(TextFieldVariant.LUMO_ALIGN_RIGHT);
    tf.setValueChangeMode(ValueChangeMode.EAGER);

    private ComboBox<Integer> combo_int;
    combo_int= new ComboBox<>();
    combo_int.setItems(114, 12383, 65432189);
    combo_int.setLabel("Some ID");
    combo_int.addCustomValueSetListener(
            event -> combo_int.setValue(Integer.parseInt(event.getDetail()))
            // since I need to parse new values here, I cannot use a validator upon binding
    );

    binder = new BeanValidationBinder<>(MyData.class);
    binder.forField(tf)
            .withNullRepresentation("")
            .withConverter(new StringToIntegerConverter("needs to be integer!"))
            .bind("tf_data_integer");

    binder.forField(combo_int)
            .bind("integer_data");
}

所以我想出了一个非常肮脏的 McGyver 解决方案...但它仍然无法 100% 工作。

主要问题是:

  1. 我必须在 addCustomValueSetListener
  2. 中解析(并因此验证)自定义输入
  3. 我无法在 bean-binder 上执行此操作,因为自定义输入是 String 和 bean-binder 需要一个 Integer
  4. 使用 withConverter(StringToInteger) 不起作用,我仍然想要 能够 select 一个整数项目 从下拉菜单中。

所以我不得不

  • 建立我自己的警告标签
  • 切换时手动重置标签 以
  • 形式显示的对象之间
  • 使用 setInvalid 突出显示错误的输入并在
  • 之后重置它
  • 劫持验证器以重置 status_label(以解决如果用户在 select 错误后使用下拉菜单时标签仍然存在的问题 - 标签保留.. .)

我仍然遇到 binder.hasChanges() 没有将无效输入注册为更改的问题,因为我们事先在解析器中捕获了它并且它永远不会进入活页夹。

也许明天我也会找到解决办法。

    public class MyForm extends Div {

    private Label status_label = new Label();

    private TextField tf;
    tf =new TextField("TF");
    tf.setWidth("100%");
    tf.setRequired(true);
    tf.addThemeVariants(TextFieldVariant.LUMO_ALIGN_RIGHT);
    tf.setValueChangeMode(ValueChangeMode.EAGER);

    private ComboBox<Integer> combo_int;
    combo_int= new ComboBox<>();
    combo_int.setItems(114, 12383, 65432189);
    combo_int.setLabel("Some ID");
    combo_int.addCustomValueSetListener((event -> {            
            if (isInteger(event.getDetail())){
                status_label.setText(""); // reset on success  
                warengruppen_id.setValue(Integer.parseInt(event.getDetail()));
                warengruppen_id.setInvalid(false);
            } else {
                status_label.setText("Custom Format-Error!"); // set to error
                status_label.getStyle().set("color", "red");
                combo_int.setInvalid(true); // red background coloring
            }
        }
      );

    binder = new BeanValidationBinder<>(MyData.class);
    binder.forField(tf)
            .withNullRepresentation("")
            .withConverter(new StringToIntegerConverter("needs to be integer!"))
            .bind("tf_data_integer");

    binder.forField(combo_int).
             withValidator(event -> {
                    // we highjack the Validator to reset the field on correct input
                    status_label.setText("");
                    return true; // never raise the validator message
                }, "")
            .withNullRepresentation(null)
            .bind("integer_data");
    }

我的最终解决方案是使用字符串在前端显示它们并构建一个自定义转换器,我在其中禁用 "setGroupingUsed" 以阻止 Vaadin 在 UI 中将千位分隔符插入我的 ID .

事实证明这比我想象的要容易,我只需要为 UI 使用与后端 + 自定义转换器不同的数据格式。

private static class Id_StringToInteger_Converter extends StringToIntegerConverter {

    public Id_StringToInteger_Converter() {
        super("Input has to be an Integer!");
    }

    @Override
    protected NumberFormat getFormat(Locale locale) {
        final NumberFormat format = super.getFormat(locale);
        format.setGroupingUsed(false); // disable thousands-seperator!
        return format;
    }
}

combo_int= new ComboBox<>();
combo_int.setItems("6583212", "114514", "879278");
combo_int.setLabel("Some ID");
combo_int.addCustomValueSetListener(event -> combo_int.setValue(event.getDetail()));


binder.forField(kreditoren_id)
      .withNullRepresentation("")
      .withConverter(new Id_StringToInteger_Converter())
      .bind("kreditoren_id");