0

I have a dynamic form where the user may add as many ComboBox as they need. All ComboBoxes contain the same predefined list of values. Is it possible somehow with the binding and validation mechanism to prevent the user from selecting the same value in two or more ComboBoxes? Show validation error in such case.

UPDATED

ComboBox<CompositeEntityResult> locationComboBox = new ComboBox<>("Location");
        locationComboBox.setRequiredIndicatorVisible(true);
        if (focus) {
            locationComboBox.focus();
        }
        locationComboBox.setItems(query -> {
            String searchQuery = createComboBoxSearchQuery(locationComboBox, query);
            VaadinUtils.invalidateQueryOffsetAndLimit(query);
            return locationService.findByNamePatternSorted(searchQuery, alreadyAddedLocationIds, alreadyAddedLocationUnknownNames, VaadinUtils.getCurrentLocaleIso6391(), AUTOCOMPLETE_PAG, AUTOCOMPLETE_PAGE_SIZE).stream();
        });
        locationComboBox.setAllowCustomValue(true);
        locationComboBox.addCustomValueSetListener(e -> {
            locationComboBox.setValue(new CompositeEntityResult(new Skill(e.getDetail(), null)));
        });
        locationComboBox.setItemLabelGenerator(e -> TranslationUtils.getTranslatedName(e.getNode()));
        locationComboBox.setRenderer(createCompositeEntityResultComboBoxRenderer(locationComboBox));
        locationComboBox.addValueChangeListener(v -> {
            addAlreadyAddedIdAndName(v.getValue(), alreadyAddedLocationIds, alreadyAddedLocationUnknownNames);
        });
        locationComboBox.getStyle().set("--vaadin-combo-box-overlay-width", "48em");
        binder.forField(locationComboBox)
                .asRequired("Specify location")
                .bind(dto -> locationCompositeEntityResult, (dto, v) -> {
                    if (v != null) {
                        Long locationId = v.getNode().getId();
                        if (locationId != null) {
                            dto.addAddedLocationId(locationId);
                        } else {
                            dto.addUnknownLocation(v.getNode().getName());
                        }
                    }
                });

I partially solved the issue and collect IDs in addValueChangeListener, then filter them on the database level. But the issue is that I need to allow user custom input as well. So I don't know how to prevent the same custom values right now.

alexanoid
  • 24,051
  • 54
  • 210
  • 410

1 Answers1

0

Sounds like a job for bean level validation

@Test
public void beanLevelValidation() {

    final Bean bean = new Bean();
    final Binder<Bean> binder = new Binder<>(Bean.class);
    binder.setBean(bean);
    binder.withValidator(item ->
             item.getLocations().stream().distinct().count() == item.getLocations().size(),
            "Items have to be unique"
    );

    ComboBox<Object> locationComboBox = new ComboBox<>("Location");
    binder.forField(locationComboBox)
            .bind(
                    compositeEntityResult -> null,
                    (item, location) -> item.getLocations().add(location)
            );

    locationComboBox.setValue("sameValue");
    locationComboBox.setValue("differentValue");
    locationComboBox.setValue("sameValue");

    final BinderValidationStatus<Bean> validationStatus = binder.validate();
    assertThat(validationStatus.getValidationErrors().stream()
            .map(ValidationResult::getErrorMessage)
            .collect(Collectors.toList()), hasItem("Items have to be unique")
    );
}