Get All Binder Warnings

Is it possible to get all the ErrorLevel.WARNING from a Binder object? I know that

binder.validate().getFieldValidationErrors();

Can get me all of the validation ErrorLevel.ERROR, but I want a way to display all of the warnings as well. Basically in Vaaddin 7 we used the setInvalidAllowed(true) in order to allow users to save a form while invalid so they could start it and then come back later before moving it forward. I converted to the new Binder and have all the new validators working, however there is no similiar setInvalidAllow method I know of and I cannot replicate the functionality. My though was to change all my new Validator Code to create warnings instead of errors and I could just that the warnings were empty in order to push forward while still allowing them to save with some invalid data. After setting them all to

return ValidationResult.create("Error Text Here", ErrorLevel.WARNING);

I don’t know anyway to get a list of all of them. They are showing as warnings in the UI side as expected and allow the saving, but I need a way to capture them. Is this possible?

Thanks

You can use the getValidationResults() method:

List<ValidationResult> warnings = binder.validate().getBeanValidationResults().stream()
        .filter(r -> ErrorLevel.WARNING.equals(r.getErrorLevel()))
        .collect(Collectors.toList());

Hi Alejandro,

Thanks for the response. My validations are at the field level so I don’t believe they will be picked up in Bean Validations. I spend most of the day looking at the code that does the validation, in particular validateBindings() in Binder validate process. To me it looks like it removes the messages of anything that is not at the ERROR level during BindingImpl::doValidation. Do you know of anyway to get the field level warnings?

Thanks again.

Doug Yachera:
… My validations are at the field level …

What do you mean by that?

I am creating custom fields and then when I bind I am doing .withValidator(... . Maybe that does not matter in this case, however when I do what you suggested it still only picks up validators that return ValidationResult.error and not ones that return ValidationResult.create("warn message, ErrorLevel.WARNING)"

Right. According to the [Javadocs]
(https://vaadin.com/api/com/vaadin/data/ValidationResult.html#create-java.lang.String-com.vaadin.shared.ui.ErrorLevel-), “Results with ErrorLevel of INFO or WARNING are not errors by default”. Have you tried with the [setValidationStatusHandler method]
(https://vaadin.com/api/com/vaadin/data/Binder.html#setValidationStatusHandler-com.vaadin.data.BinderValidationStatusHandler-)?

Briefly, I will try looking more into that today, thanks.

Good. You could try something like the following:

binder.validate();
binder.setValidationStatusHandler(statusChange -> {
    List<ValidationResult> warnings = statusChange.getBeanValidationResults().stream()
            .filter(r -> ErrorLevel.WARNING.equals(r.getErrorLevel()))
            .collect(Collectors.toList());
});

It looks like even after overriding the status change with

    @Override
	protected
    void handleBinderValidationStatus(BinderValidationStatus<BEAN> binderStatus){
    	List<ValidationResult> test1 = binderStatus.getBeanValidationErrors();
    	List<ValidationResult> test2 = binderStatus.getBeanValidationResults();
    	List<BindingValidationStatus<?>> test3 = binderStatus.getFieldValidationErrors();
    	List<BindingValidationStatus<?>> test4 = binderStatus.getFieldValidationStatuses();
    	List<ValidationResult> test5 = binderStatus.getValidationErrors();
    	
    	System.out.println("===============================================================");
    	test1.stream().forEach(val -> System.out.println(val.getErrorMessage()));
    	System.out.println("===============================================================");
    	test2.stream().forEach(val -> System.out.println(val.getErrorMessage()));
    	System.out.println("===============================================================");
    	test3.stream().forEach(val -> val.getMessage().ifPresent(msg -> System.out.println(msg)));
    	System.out.println("===============================================================");
    	test4.stream().forEach(val -> val.getMessage().ifPresent(msg -> System.out.println(msg)));
    	System.out.println("===============================================================");
    	test5.stream().forEach(val -> System.out.println(val.getErrorMessage()));
    	System.out.println("===============================================================");
    	super.handleBinderValidationStatus(binderStatus);
    }

All warnings are gone. After looking at the internal code when they create the ValidationResult, if it is not an ERROR they do not pass the message to the result so you lose the information when you get to that point…

    ValidationResultWrap(R value, ValidationResult result) {
        if (result.isError()) {
            wrappedResult = new SimpleResult<>(null, result.getErrorMessage());
        } else {
            wrappedResult = new SimpleResult<>(value, null);  // Message is not passed at this point
        }
        this.resultList = new ArrayList<>();
        this.resultList.add(result);
    }

For now I am extending the Binder to get access to getBindings() and doing the following which prints the warnings:

	private Collection<BindingImpl<BEAN, ?, ?>> getAllBindings(){
		return super.getBindings();
	}
	
	    public void test2(){
    	for(BindingImpl<BEAN, ?, ?> bind : getAllBindings()){
    		HasValue<?> field = bind.getField();
    		if(field instanceof AbstractComponent){
    			System.out.println(((AbstractComponent)field).getErrorMessage() == null ? "Empty" : ((AbstractComponent)field).getErrorMessage().toString());
    		}
    	}
    }

I would love to see a method added to Binder itself to get all the warnings internally.

Alright. My bad. I didn’t realize ValidationResult.getErrorLevel() returns an Optional. I got it working now. Here’s what you can use:

binder.setValidationStatusHandler(statusChange -> {
    for (BindingValidationStatus<?> change : statusChange.getFieldValidationStatuses()) {
        List<ValidationResult> results = change.getValidationResults();
        for (ValidationResult r : results) {
            if (ErrorLevel.WARNING.equals(r.getErrorLevel().orElse(null))) {
                Notification.show(r.getErrorMessage());
            }
        }
    }
});

binder.validate();

Here’s a complete example: https://gist.github.com/alejandro-du/576474c5803205032f89af4e75c50f72

Or if you preffer streams:

binder.setValidationStatusHandler(statusChange ->
        statusChange.getFieldValidationStatuses().stream()
                .map(r -> r.getValidationResults())
                .flatMap(l -> l.stream())
                .filter(r -> ErrorLevel.WARNING.equals(r.getErrorLevel().orElse(null)))
                .forEach(w -> Notification.show(w.getErrorMessage()))
);

binder.validate();

Thanks, that is exactly what I was looking for

Good to hear. Happy coding!