CustomField width in a FormLayout

Hi,

I am migrating a vaadin 8 project to Vaadin 24 Flow, so far it works great, but I have an issue using the new FormLayout, I love the possibility to have multiple columns, and the auto computation for the width of the columns is very nice too.

But I have a component that doesn’t seems to behave well in this case :

image

Here I have zoomed on the component, the vertical position of the checkbox is not an issue, here is the issue :

image

Here, I have made the form less wide using a splitter on the left of the formlayout, my issue is that my component is displayed over the components that are on the second column on the right.

My component is a CustomField, and it consist of a horizontallayout with 3 IntegerField and a Checkbox.

Maybe there is an issue computing the width of my component, but I am not sure how to solve that.

What should I do ? Best regards.

Could you share the code you use to produce that form, and ideally the CustomField as well?

I’m especially curious about the Inactivité label, and the way both the checkbox and the customfield, which should line up correctly, seem to be aligned with that label instead of the other fields.

Are the Jour, Heure and Minute fields themselves wrapped in a CustomField perhaps? That will not work well in a FormLayout, since they have their own labels which push them down from the baseline they’re supposed to be on, so you’d need to either remove their labels (and maybe use helpers instead), or unwrap them.

The fields are just IntegerField but by using a class that extends it to help setting them correctly, please disregard the vertical issues, I guess we will be able to solve that using css.

Here is the code of the custom IntegerField :

public class I18NIntStepper extends IntegerField
{
	public I18NIntStepper()
	{
	}

	public I18NIntStepper(final String caption, final Long min, final Long max, final Integer step, final boolean nullable)
	{
		super(I18NBundle.convert(caption));
		init(min, max, nullable);
	}

	@Override
	public void setLabel(String label)
	{
		super.setLabel(I18NBundle.convert(label));
	}

	private void init(Long min, final Long max, boolean nullable)
	{
		setStepButtonsVisible(true);
		this.setMin(min.intValue());
		this.setMax(max.intValue());
		this.setValue(null);
		this.setStep(1);
		this.setWidth("150px");
	}
}

Here is the code for the component :

public class Steppers extends CustomField<Long>
{
	private static final Long ONE_YEAR = 365L;

	@PropertyId("day")
	private I18NIntStepper intSDay;

	@PropertyId("hour")
	private I18NIntStepper intSHour;

	@PropertyId("minute")
	private I18NIntStepper intSMin;

	@PropertyId("second")
	private I18NIntStepper intSSec;

	@PropertyId("millisecond")
	private I18NIntStepper intSMs;

	@PropertyId("negative")
	private final I18NCheckBox cbDisabled = new I18NCheckBox("Disabled");

	private final List<I18NIntStepper> listFields = new ArrayList<>();

	private final HorizontalLayout layout = new HorizontalLayout();

	private Binder<Time> binder;

	public Steppers()
	{
		add(v8_initContent());
	}

	@Override
	protected Long generateModelValue()
	{
		return v8_getValue();
	}

	@Override
	protected void setPresentationValue(Long newPresentationValue)
	{
		v8_doSetValue(newPresentationValue);
	}

	public Steppers(final String caption, final Granularity granularity, final Long min, final Long max, final Integer step, final boolean nullable, final boolean enabled)
	{
		this();
		super.setLabel(caption);
		initFields(granularity, min, max, step, nullable, enabled);
	}

	public Steppers(Granularity granularity, long steppersMin, long steppersMax, int step, boolean nullable, boolean enabled)
	{
		this(null, granularity, steppersMin, steppersMax, step, nullable, enabled);
	}

	public void initFields(final Granularity granularity, final Long min, final Long max, final Integer step, final boolean nullable, final boolean enabled)
	{
		switch (granularity)
		{
			case JHMIN:
				intSDay = new I18NIntStepper("Day", 0L, ONE_YEAR, step, nullable);
				intSHour = new I18NIntStepper("Hour", 0L, TimeUnit.DAYS.toHours(1) - 1, step, nullable);
				intSMin = new I18NIntStepper("Minute", 0L, TimeUnit.HOURS.toMinutes(1) - 1, step, nullable);

				addFields(intSDay, intSHour, intSMin);
				break;
			case MIN:
				intSMin = new I18NIntStepper("Minute", 0L, TimeUnit.HOURS.toMinutes(1) - 1, step, nullable);
				addFields(intSMin);
				break;
			case MINSEC:
				intSSec = new I18NIntStepper("Second", 0L, TimeUnit.MINUTES.toSeconds(1) - 1, step, nullable);
				intSMs = new I18NIntStepper("Millisecond", 0L, TimeUnit.SECONDS.toMillis(1) - 1, step, nullable);

				addFields(intSSec, intSMs);
				break;
			case SEC:
				intSSec = new I18NIntStepper("Second", 0L, TimeUnit.MINUTES.toSeconds(1) - 1, step, nullable);

				addFields(intSSec);
				break;
			default:
				intSMs = new I18NIntStepper("Millisecond", min, max, step, nullable);
				addFields(intSMs);

				break;
		}

		for (final I18NIntStepper field : listFields)
		{
			layout.add(field);
		}

		if (enabled)
		{
			layout.add(cbDisabled);
			cbDisabled.addValueChangeListener((ValueChangeEvent<Boolean> event) ->
			{
				setEnabled(!event.getValue());
				super.setEnabled(true);
				cbDisabled.setEnabled(true);
			});
		}

		binder = new Binder<>(Time.class);
		binder.bindInstanceFields(this);

		layout.setMargin(false);
	}

	public Long v8_getValue()
	{
		if (binder.getBean() != null)
		{
			return binder.getBean().getTimeMs();
		}
		else
		{
			return null;
		}
	}

	protected Component v8_initContent()
	{
		return layout;
	}

	protected void v8_doSetValue(Long value)
	{
		if (value != null)
		{
			Time t = new Time(value);
			binder.setBean(t);
		}
		else
		{
			binder.setBean(null);
		}
	}

	public void setEnabled(boolean enabled)
	{
		super.setEnabled(enabled);
		for (I18NIntStepper field : listFields)
		{
			field.setEnabled(enabled);
		}
		cbDisabled.setEnabled(enabled);
	}

	@Override
	public void setLabel(String label)
	{
		super.setLabel(I18NBundle.convert(label));
	}

	private void addFields(final I18NIntStepper... fields)
	{
		Collections.addAll(listFields, fields);
	}
}

As for the code that produce the form, it will be quite difficult to share it as it’s a programmatic form generator class that use both bean reflection and a resource file that will tell what do display in the form and how to do it.

Just for a reference, here is how it looks in the vaadin 8 app :

I noticied that the checkbox label is not displayed at the same position, I guess I need to use a theme variant to make it shown on top

I do need to get an idea of how the FormLayout is structured.
Perhaps you could share a screenshot of the html structure, as seen in the browser’s devtools?

As for the Checkbox, its label is always to the right of the box, as is usually the case with checkboxes, and there is no variant to move it elsewhere. It should, however, line up correctly in the FormLayout if everything is done correctly. If you absolutely need a label above the checkbox (which I generally don’t recommend as users are not used to that), you can use a single-item CheckboxGroup, or wrap it also in a CustomField.

Hahaha, I was indeed trying to replace the checkbox with a single item checkboxgroup to work around this, but I stopped as the getValue return a Set of boolean and it breaks our form generator as for this component we are expecting a boolean directly.

Is the html of only the form enought ? or do you want the full page code ?

The vaadin-form-layout and its children is enough.

At least now the vertical alignment is good :wink:

<vaadin-form-layout style="width: 100%; height: 100%;" data-width-full="" data-height-full="" labels-aside="" expand-columns="">
   <vaadin-checkbox class="v-switch-holodeck" id="Channel_enabled" has-value="" has-label="" style="width: calc(50% - 0.75rem); margin-left: 0px;" checked="">
      <label slot="label" id="label-vaadin-checkbox-7" for="input-vaadin-checkbox-98">Actif</label>
      <div slot="error-message" id="error-message-vaadin-checkbox-9" hidden=""></div>
      <input slot="input" type="checkbox" value="on" id="input-vaadin-checkbox-98" aria-labelledby="label-vaadin-checkbox-7" tabindex="0">
   </vaadin-checkbox>
   <vaadin-custom-field disabled="" id="Channel_id" has-label="" role="group" style="width: calc(50% - 0.75rem); margin-right: 0px;" aria-labelledby="label-vaadin-custom-field-10" has-value="">
      <vaadin-text-field disabled="" aria-disabled="true" has-value="">
         <label slot="label" id="label-vaadin-text-field-13" for="input-vaadin-text-field-99"></label>
         <div slot="error-message" id="error-message-vaadin-text-field-15" hidden=""></div>
         <input slot="input" type="text" id="input-vaadin-text-field-99" disabled="">
      </vaadin-text-field>
      <label slot="label" id="label-vaadin-custom-field-10">Référence</label>
      <div slot="error-message" id="error-message-vaadin-custom-field-12" hidden=""></div>
   </vaadin-custom-field>
   <vaadin-text-field id="Channel_name" has-label="" style="width: calc(50% - 0.75rem); margin-left: 0px;" has-value="">
      <label slot="label" id="label-vaadin-text-field-16" for="input-vaadin-text-field-100">Nom de voie</label>
      <div slot="error-message" id="error-message-vaadin-text-field-18" hidden=""></div>
      <input slot="input" type="text" id="input-vaadin-text-field-100" aria-labelledby="label-vaadin-text-field-16" tabindex="0">
   </vaadin-text-field>
   <vaadin-custom-field id="Channel_type" has-label="" role="group" style="width: calc(50% - 0.75rem); margin-right: 0px;" aria-labelledby="label-vaadin-custom-field-19" has-value="">
      <vaadin-horizontal-layout class="combobox-button" theme="spacing" has-start="">
         <vaadin-select style="width: 600px;" has-value="">
            <label slot="label" id="label-vaadin-select-22"></label>
            <div slot="error-message" id="error-message-vaadin-select-24" hidden=""></div>
            <vaadin-select-value-button slot="value" tabindex="0" role="button" aria-expanded="false" aria-haspopup="listbox" aria-labelledby="value-vaadin-select-25">
               <vaadin-select-item id="value-vaadin-select-25" selected="" aria-selected="true">Analogique - Dahdi</vaadin-select-item>
            </vaadin-select-value-button>
            <label slot="sr-label" id="label-vaadin-select-26"></label>
         </vaadin-select>
         <vaadin-button tabindex="0" role="button" last-start-child="">Configure</vaadin-button>
      </vaadin-horizontal-layout>
      <label slot="label" id="label-vaadin-custom-field-19">Type</label>
      <div slot="error-message" id="error-message-vaadin-custom-field-21" hidden=""></div>
   </vaadin-custom-field>
   <vaadin-text-field id="Channel_userName" has-label="" style="width: calc(50% - 0.75rem); margin-left: 0px;">
      <label slot="label" id="label-vaadin-text-field-27" for="input-vaadin-text-field-101">Nom de la personne enregistrée</label>
      <div slot="error-message" id="error-message-vaadin-text-field-29" hidden=""></div>
      <input slot="input" type="text" id="input-vaadin-text-field-101" aria-labelledby="label-vaadin-text-field-27" tabindex="0">
   </vaadin-text-field>
   <vaadin-text-field id="Channel_userNumber" has-label="" style="width: calc(50% - 0.75rem); margin-right: 0px;">
      <label slot="label" id="label-vaadin-text-field-30" for="input-vaadin-text-field-102">N° de poste de la personne enregistrée</label>
      <div slot="error-message" id="error-message-vaadin-text-field-32" hidden=""></div>
      <input slot="input" type="text" id="input-vaadin-text-field-102" aria-labelledby="label-vaadin-text-field-30" tabindex="0">
   </vaadin-text-field>
   <vaadin-custom-field id="Channel_durationMin" has-label="" role="group" has-value="" style="width: calc(50% - 0.75rem); margin-left: 0px;" aria-labelledby="label-vaadin-custom-field-33">
      <vaadin-horizontal-layout theme="spacing" has-start="">
         <vaadin-integer-field style="width: 150px;" step-buttons-visible="" has-label="" has-value="">
            <label slot="label" id="label-vaadin-integer-field-36" for="input-vaadin-integer-field-103">Seconde</label>
            <div slot="error-message" id="error-message-vaadin-integer-field-38" hidden=""></div>
            <input slot="input" type="number" id="input-vaadin-integer-field-103" step="1" min="0" max="59" aria-labelledby="label-vaadin-integer-field-36" tabindex="0">
         </vaadin-integer-field>
         <vaadin-integer-field style="width: 150px;" step-buttons-visible="" has-label="" has-value="">
            <label slot="label" id="label-vaadin-integer-field-39" for="input-vaadin-integer-field-104">Milliseconde</label>
            <div slot="error-message" id="error-message-vaadin-integer-field-41" hidden=""></div>
            <input slot="input" type="number" id="input-vaadin-integer-field-104" step="1" min="0" max="999" aria-labelledby="label-vaadin-integer-field-39" tabindex="0">
         </vaadin-integer-field>
         <vaadin-custom-field class="steppers-check" has-label="" role="group" has-value="" aria-labelledby="label-vaadin-custom-field-42" last-start-child="">
            <vaadin-checkbox has-value="">
               <label slot="label" id="label-vaadin-checkbox-45" for="input-vaadin-checkbox-105"></label>
               <div slot="error-message" id="error-message-vaadin-checkbox-47" hidden=""></div>
               <input slot="input" type="checkbox" value="on" id="input-vaadin-checkbox-105" tabindex="0">
            </vaadin-checkbox>
            <label slot="label" id="label-vaadin-custom-field-42">Disabled</label>
            <div slot="error-message" id="error-message-vaadin-custom-field-44" hidden=""></div>
         </vaadin-custom-field>
      </vaadin-horizontal-layout>
      <label slot="label" id="label-vaadin-custom-field-33">Durée minimum</label>
      <div slot="error-message" id="error-message-vaadin-custom-field-35" hidden=""></div>
   </vaadin-custom-field>
   <vaadin-custom-field id="Channel_durationMax" has-label="" role="group" style="width: calc(50% - 0.75rem); margin-right: 0px;" aria-labelledby="label-vaadin-custom-field-48">
      <vaadin-horizontal-layout theme="spacing" has-start="">
         <vaadin-integer-field style="width: 150px;" step-buttons-visible="" has-label="" last-start-child="" has-value="">
            <label slot="label" id="label-vaadin-integer-field-51" for="input-vaadin-integer-field-106">Minute</label>
            <div slot="error-message" id="error-message-vaadin-integer-field-53" hidden=""></div>
            <input slot="input" type="number" id="input-vaadin-integer-field-106" step="1" min="0" max="59" aria-labelledby="label-vaadin-integer-field-51" tabindex="0">
         </vaadin-integer-field>
      </vaadin-horizontal-layout>
      <label slot="label" id="label-vaadin-custom-field-48">Durée maximum</label>
      <div slot="error-message" id="error-message-vaadin-custom-field-50" hidden=""></div>
   </vaadin-custom-field>
   <vaadin-custom-field id="Channel_inactivityAlarm" has-label="" role="group" has-value="" style="width: calc(50% - 0.75rem); margin-left: 0px;" aria-labelledby="label-vaadin-custom-field-54">
      <vaadin-horizontal-layout theme="spacing" has-start="">
         <vaadin-integer-field style="width: 150px;" step-buttons-visible="" has-label="" has-value="">
            <label slot="label" id="label-vaadin-integer-field-57" for="input-vaadin-integer-field-107">Jour</label>
            <div slot="error-message" id="error-message-vaadin-integer-field-59" hidden=""></div>
            <input slot="input" type="number" id="input-vaadin-integer-field-107" step="1" min="0" max="365" aria-labelledby="label-vaadin-integer-field-57" tabindex="0">
         </vaadin-integer-field>
         <vaadin-integer-field style="width: 150px;" step-buttons-visible="" has-label="" has-value="">
            <label slot="label" id="label-vaadin-integer-field-60" for="input-vaadin-integer-field-108">Heure</label>
            <div slot="error-message" id="error-message-vaadin-integer-field-62" hidden=""></div>
            <input slot="input" type="number" id="input-vaadin-integer-field-108" step="1" min="0" max="23" aria-labelledby="label-vaadin-integer-field-60" tabindex="0">
         </vaadin-integer-field>
         <vaadin-integer-field style="width: 150px;" step-buttons-visible="" has-label="" has-value="">
            <label slot="label" id="label-vaadin-integer-field-63" for="input-vaadin-integer-field-109">Minute</label>
            <div slot="error-message" id="error-message-vaadin-integer-field-65" hidden=""></div>
            <input slot="input" type="number" id="input-vaadin-integer-field-109" step="1" min="0" max="59" aria-labelledby="label-vaadin-integer-field-63" tabindex="0">
         </vaadin-integer-field>
         <vaadin-custom-field class="steppers-check" has-label="" role="group" has-value="" aria-labelledby="label-vaadin-custom-field-66" last-start-child="">
            <vaadin-checkbox has-value="">
               <label slot="label" id="label-vaadin-checkbox-69" for="input-vaadin-checkbox-110"></label>
               <div slot="error-message" id="error-message-vaadin-checkbox-71" hidden=""></div>
               <input slot="input" type="checkbox" value="on" id="input-vaadin-checkbox-110" tabindex="0">
            </vaadin-checkbox>
            <label slot="label" id="label-vaadin-custom-field-66">Disabled</label>
            <div slot="error-message" id="error-message-vaadin-custom-field-68" hidden=""></div>
         </vaadin-custom-field>
      </vaadin-horizontal-layout>
      <label slot="label" id="label-vaadin-custom-field-54">Inactivité</label>
      <div slot="error-message" id="error-message-vaadin-custom-field-56" hidden=""></div>
   </vaadin-custom-field>
   <vaadin-custom-field disabled="" id="Channel_r17Index" has-label="" role="group" style="width: calc(50% - 0.75rem); margin-right: 0px;" aria-labelledby="label-vaadin-custom-field-72" has-value="">
      <vaadin-text-field disabled="" aria-disabled="true" has-value="">
         <label slot="label" id="label-vaadin-text-field-75" for="input-vaadin-text-field-111"></label>
         <div slot="error-message" id="error-message-vaadin-text-field-77" hidden=""></div>
         <input slot="input" type="text" id="input-vaadin-text-field-111" disabled="">
      </vaadin-text-field>
      <label slot="label" id="label-vaadin-custom-field-72">Index R17</label>
      <div slot="error-message" id="error-message-vaadin-custom-field-74" hidden=""></div>
   </vaadin-custom-field>
   <vaadin-text-field id="Channel_cstaId" has-label="" style="width: calc(50% - 0.75rem); margin-left: 0px;">
      <label slot="label" id="label-vaadin-text-field-78" for="input-vaadin-text-field-112">ID Csta</label>
      <div slot="error-message" id="error-message-vaadin-text-field-80" hidden=""></div>
      <input slot="input" type="text" id="input-vaadin-text-field-112" aria-labelledby="label-vaadin-text-field-78" tabindex="0">
   </vaadin-text-field>
   <vaadin-select id="Channel_group" has-label="" style="width: calc(50% - 0.75rem); margin-right: 0px;">
      <label slot="label" id="label-vaadin-select-81">Groupe</label>
      <div slot="error-message" id="error-message-vaadin-select-83" hidden=""></div>
      <vaadin-select-value-button slot="value" tabindex="0" role="button" aria-expanded="false" aria-haspopup="listbox" aria-labelledby="label-vaadin-select-81"></vaadin-select-value-button>
      <label slot="sr-label" id="label-vaadin-select-85"></label>
   </vaadin-select>
   <vaadin-radio-group id="Channel_speechToText" has-label="" role="radiogroup" style="width: calc(50% - 0.75rem); margin-left: 0px;" aria-labelledby="label-vaadin-radio-group-86" has-value="">
      <vaadin-radio-button has-value="" has-label="" checked=""><label slot="label" id="label-vaadin-radio-button-89" for="input-vaadin-radio-button-114"><span>Héritage à partir du groupe (ou désactivé si pas de groupe)</span></label><input slot="input" type="radio" value="1" id="input-vaadin-radio-button-114" tabindex="0" name="vaadin-radio-group-113"></vaadin-radio-button>
      <vaadin-radio-button has-value="" has-label=""><label slot="label" id="label-vaadin-radio-button-90" for="input-vaadin-radio-button-115"><span>Activé</span></label><input slot="input" type="radio" value="2" id="input-vaadin-radio-button-115" tabindex="0" name="vaadin-radio-group-113"></vaadin-radio-button>
      <vaadin-radio-button has-value="" has-label=""><label slot="label" id="label-vaadin-radio-button-91" for="input-vaadin-radio-button-116"><span>Désactivé</span></label><input slot="input" type="radio" value="3" id="input-vaadin-radio-button-116" tabindex="0" name="vaadin-radio-group-113"></vaadin-radio-button>
      <label slot="label" id="label-vaadin-radio-group-86">Reconnaissance vocale</label>
      <div slot="error-message" id="error-message-vaadin-radio-group-88" hidden=""></div>
   </vaadin-radio-group>
   <vaadin-radio-group id="Channel_filtering" disabled="" aria-disabled="true" has-label="" role="radiogroup" style="width: calc(50% - 0.75rem); margin-right: 0px;" aria-labelledby="label-vaadin-radio-group-92" has-value="">
      <vaadin-radio-button has-value="" has-label="" checked="" disabled="" aria-disabled="true"><label disabled="" slot="label" id="label-vaadin-radio-button-95" for="input-vaadin-radio-button-118"><span disabled="">Héritage à partir du groupe (ou désactivé si pas de groupe)</span></label><input slot="input" type="radio" value="1" id="input-vaadin-radio-button-118" tabindex="-1" name="vaadin-radio-group-117" disabled=""></vaadin-radio-button>
      <vaadin-radio-button has-value="" has-label="" disabled="" aria-disabled="true"><label disabled="" slot="label" id="label-vaadin-radio-button-96" for="input-vaadin-radio-button-119"><span disabled="">Activé</span></label><input slot="input" type="radio" value="2" id="input-vaadin-radio-button-119" tabindex="-1" name="vaadin-radio-group-117" disabled=""></vaadin-radio-button>
      <vaadin-radio-button has-value="" has-label="" disabled="" aria-disabled="true"><label disabled="" slot="label" id="label-vaadin-radio-button-97" for="input-vaadin-radio-button-120"><span disabled="">Désactivé</span></label><input slot="input" type="radio" value="3" id="input-vaadin-radio-button-120" tabindex="-1" name="vaadin-radio-group-117" disabled=""></vaadin-radio-button>
      <label slot="label" id="label-vaadin-radio-group-92">Filtres</label>
      <div slot="error-message" id="error-message-vaadin-radio-group-94" hidden=""></div>
   </vaadin-radio-group>
</vaadin-form-layout>

I hope the formatter I used didn’t break it

Thanks, no problem to read that.

And it is as I suspected: your fields are not children of the FormLayout, but of a HorizontalLayout inside a CustomField, which itself is a child of FormLayout. That ruins both the vertical alignment and the column-layouting of the form. It’s not a valid way to use the FormLayout.

The fields (whether CustomField or others) need to be direct children of the FormLayout for it to work. There is, unfortunately, no way to create labelled “field groups” in the way I think you’re trying to do. For that, I would recommend using a separate FormLayout for each group instead. Or, alternatively, you could add the group label as a Span or something with colspan set to something really big – that way it will end up on its own row above the fields that belong to it.

Also, if you’re on V24.8 or later, I recommend using the new autoResponsive mode and breaking up the form into FormRows.

Something like this could work for you:

CustomField with label Inactivité
    IntegerField with helper Jour
    IntegerField with helper Heure
    IntegerField with helper Minute

or, even better:

Span with Inactivité with some big colspan
IntegerField with label Jour
IntegerField with label Heure
IntegerField with label Minute     

Also note that if your CustomField with the 3 subfields is longer than an actual FormLayout column, it will not automatically span multiple columns, but instead break the layout. So if you need to have the 3 fields wrapped in a CustomField for databinding, you need to set colspan=3 or something on the CustomField for it to span 3 columns.

I guess using a span intead of the horizontallayout is a the solution, but the fields are now seen as individual components when I resize the form, as they can go to a new row. Is there a way to make the span consider that there is only one row that can’t be splitted into multiple rows ?

image

If the fields don’t fit side-by-side, they need to wrap – that’s kind of the point of FormLayout.

Ah, I just realized I made a mistake in my suggestion above: the span should not contain the fields, it should be a FormLayout child before the fields. The fields should be direct children of the FormLayout for the layout to work correctly.

(I’ve now corrected the structure above)

Right now the Span works well, and the fact that all the int fields can go to a new line is not that an issue, it could be a good thing actually. I just need to find a way to make the int fields have a little spacing between them when on the same line.

Thank you very much for your help.

They would normally have spacing between them, so if they don’t, it’s probably because they are wider than an actual column. Instead of setting fields to be 150px wide, I would recommend setting them to 100% and making sure the FormLayout’s column width is appropriate for them.

If you use the default mode, it’s with the responsiveSteps API: Form Layout component | Vaadin components

But I would recommend using the autoResponsive mode, which has a simpler API: Form Layout component | Vaadin components
Then you can just do formLayout.setColumnWidth("150px"); if you want all fields to be 150px, and formLayout.setExpandFields(true); to make all fields match that width by default.

Something like this:

var form = new FormLayout();
form.setAutoResponsive(true);
form.setColumnWidth("150px");
form.setExpandFields(true);
form.addFormRow(new Span("Inactivité"));
form.addFormRow(jourField, heureField, minuteField, desactiveCheckbox);

When I use autoResponsive I only have one column, I tried with setting a form column width and with or without expandFields, so I will stay with autoResponsive to false as even if the old ui was with only one big column, we like how the form can now have multiple column, it makes the ui more compact.

Unless I update my instruction file, I won’t be able to use the addFormRow methods as I don’t have any possibility right now to tells my generator to use addFormRow instead of just using a bunch of add(…)

I really just have an issue with the fields not having a spacing between them like the do in a horizontallayout, but I guess I will find a way, maybe with custom css if needed.

I forced them to 150px to make them smaller as we only display a few digits at best and they were too big, if I force them to 100% they do that :

image

I am not sure how to do what I want, I really wish this could be seen as a single component, everything on the same line.

I am not sure if it’s clear but the “Inactivité” text is the label of the component, the component that consists of the 3 int fields and the checkbox

Yes, that is clear. It’s just that FormLayout only works with fields in the root of the layout, so if you want to have them wrapped in a HorizontalLayout and don’t care if they wrap, you don’t have much use for FormLayout at all.

Perhaps it would be better to just use a VerticalLayout with a bunch of HorizontalLayouts instead? You’re kind of working against FormLayout instead of benefiting from it otherwise.

I see, we are using form layout as it handle the input validation and was very useful for that.

Hmm, FormLayout is really just a layout though – validation happens in the field components themselves and/or in Binder, so you’re no worse off with a VerticalLayout.

I suppose the V8 FormLayout had its own way of displaying validation errors, but that’s no longer the case in V24.