Programmatically setting a value on a ComboBox

Hi,

I’m trying to set a selected value programmatically (based on a default selection stored in the database) on a ComboBox.

The strange this is, after I’ve set the items, and set the default value using setValue() I see in the valueChangeListener that first the value that I’ve set is coming in, but then that is immediately followed by a null value.

I have no idea where that null value is coming from.

api.foo().thenAccept(fooList -> {

			final Foo foo = fooList.get(0);

			ui.access(() -> {
				myCombobox.setItems(fooList);
				myCombobox.setValue(foo);
			});

			myCombobox.addValueChangeListener(event -> {
				final Foo selectedFoo = event.getValue();
// do something
			});
		});

api.foo() is potentially slow, so I made it async.

This block is executed in the onAttach method of the view, so I get the ui reference from the attachEvent.

Any ideas what might be causing this?

There might be a logical explanation for this, but I simply don’t see it.

Thanks!

Kristof

Just the obvious question: does it also happen in a small reproducible example?

I have had similar experiences when using nullrepresentation on the Binding. Maybe you have a nullrepresentation set to empty string, and the first foo of your list is also an empty string?

Can I make a small example? Yes
Can I reproduce it in that example? No

:exploding_head:

Might be related to push… about how long are we talking about? 1-2 seconds that are just annoying while loading the form or more?

If it’s just the first: you can also use a focus listener and only load the data if the user clicks in the field (you still need to quickly load the first value if that always has to be selected)

If that’s not possible, it might help to move the value change listener to the Combobox init

The combobox is also showing the user what is actually selected.

This is the event I get after I’ve set the value server-side.

This is what comes immediately after that:

How can fromClient be true if this is just on page load, and I’m not actually clicking the combobox?

I’m completely lost here.

I did loose the valueChangeEvent with event.getValue()==null when adding the changeListener to the init of the ComboBox.

So, now I get a valueChangeEvent with the selected value: basically showing a notification with the value that is supposed to be set, but the combobox remains blank.

I guess it’s a bit of progress.

Probably the async part.

Hi @ollit.1 ,

I was inspired by this youtube tutorial by @marcushellberg .

I just replaced the ListenableFuture implementation with Spring @Async annotation.

This api call is a REST call, I assumed that doing it this way is actually best practice. Doesn’t seem right that I would do a call to a remote system synchronously from the onAttach method to load data into a component. After all, combobox doesn’t have a FetchCallback or BackendCallback like Grid does.

Small update:

I just discovered that this probably is a timing issue, and not the async per se.

If I do setItems() immediately followed by setValue(), then the valueChangeEvent is triggered, but the value is not shown as the selected value on the combobox.

However when I add a Thread.sleep in between the two, then it works.

	@Override
	protected void onAttach(AttachEvent attachEvent) {

		final var ui = attachEvent.getUI();

		explore.indexes().thenAccept(indexes -> {
			ui.access(() -> selectIndex.setItems(indexes));
			try {
				Thread.sleep(1000);
				ui.access(()-> selectIndex.setValue(indexes.get(0)));
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
		});
	}

Pre-fetching everything is not an option given the amount of these cases I will have in this application eventually and also, now I just select first from the list, but there will be logic behind which one to select as well (user preferences and fallback to system default, which will also require a DB call)

Or am I overengineering this? And should I not worry about doing synchronous REST / DB calls in the onAttach method?

Thank you for your feedback!

Kind regards,

Kristof.

Not sure what’s the correct way here, but multithreading makes the control flow complicated to understand. If you make a change to a component’s state (such as setting the value) once the request thread has completed, it will only be updated to the browser if you have Push enabled or once the next request arrives and returns. Sometimes you can make changes to a Component from a non-request thread during the request’s lifetime, and it works by accident, but once there’s a delay in the thread, it suddenly stops working without obvious explanation. Using language or framework features that hide the threading, such as @Async or asynchronous Events, tend to make things even more difficult to see.

Quick Note: don’t do it in onAttach; use the Afternavigation observer… this might already fix all problems