Wrapping required TextField with FormItem breaks indicator behaviour

Hello, everyone!

I’ve faced strange befavior with required indicator. I hope you can help me to understand where is a problem.

I have next view.

@SuppressWarnings("serial")
@PageTitle("test")
@Route(value = TestView.ROUTE, layout = MainLayout.class)
@RolesAllowed({ UserRole.ADMIN })
public class TestView extends FormLayout {

    private static final String ROUTE = "test";

    public TestView() {
	super();
	initComponents();
    }

    public void initComponents() {
	TextField textField = new TextField("text field");
	textField.setRequired(true);
	add(textField);
	
	TextField textField2 = new TextField();
	textField2.setRequired(true);
	addFormItem(textField2, "form item");
    }
}

image

when I leave “text field” component the required indicator is blue - it’s ok for me
but when I leave “form item” component the required indicator is red for some reason

I’ve tried to debug it but losing a focus of the component doesn’t trigger any change events so validation isn’t used. (debugger won’t stop there)
image

Not sure how to workaround this, but it appears a few things are happening:

  • The invalid flag on the <vaadin-form-item> controls the required indicator’s color, but does not affect the input field (in this case the <vaadin-text-field>) which must have its own invalid flag updated
  • The <vaadin-form-item> element’s invalid property is updated on blur, even if the user does not modify the value
  • The <vaadin-text-field> element’s invalid property is updated only if the user modifies the input value

The listeners are happening in the javascript source of the form item - vaadin-form-item.js (you can find this in your node_modules):

/** @private */
__updateRequiredState(required) {
  if (required) {
    this.setAttribute('required', '');
    this.__fieldNode.addEventListener('blur', this.__updateInvalidState);
    this.__fieldNode.addEventListener('change', this.__updateInvalidState);
  } else {
    this.removeAttribute('invalid');
    this.removeAttribute('required');
    this.__fieldNode.removeEventListener('blur', this.__updateInvalidState);
    this.__fieldNode.removeEventListener('change', this.__updateInvalidState);
  }
}

/** @private */
__updateInvalidState() {
  const isValid = this.__getValidateFunction(this.__fieldNode).call(this.__fieldNode);
  this.toggleAttribute('invalid', isValid === false);
}

Thank you for your attention to this problem!

For now I decided to hide these marks since I have validation anyway.